Blame progit/ui/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 be7090
import shutil
Pierre-Yves Chibon 41558c
import tempfile
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 6bf4e3
import progit.doc_utils
Pierre-Yves Chibon 80bba3
import progit.lib
Pierre-Yves Chibon ac8023
import progit.forms
Pierre-Yves Chibon 1e5fe9
from progit import (APP, SESSION, LOG, __get_file_in_tree, cla_required,
Pierre-Yves Chibon 59be40
                    is_repo_admin, generate_gitolite_acls)
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon b31474
@APP.route('/<repo>/request-pulls')</repo>
Pierre-Yves Chibon b31474
@APP.route('/fork/<username>/<repo>/request-pulls')</repo></username>
Pierre-Yves Chibon b31474
def request_pulls(repo, username=None):
Pierre-Yves Chibon b31474
    """ Request pulling the changes from the fork into the project.
Pierre-Yves Chibon 47950c
    """
Pierre-Yves Chibon b31474
    status = flask.request.args.get('status', True)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
    repo = progit.lib.get_project(SESSION, repo, user=username)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
    if not repo:
Pierre-Yves Chibon 47950c
        flask.abort(404, 'Project not found')
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon b31474
    if status is False or str(status).lower() == 'closed':
Pierre-Yves Chibon b31474
        requests = progit.lib.get_pull_requests(
Pierre-Yves Chibon b31474
            SESSION, project_id=repo.id, status=False)
Pierre-Yves Chibon 47950c
    else:
Pierre-Yves Chibon b31474
        requests = progit.lib.get_pull_requests(
Pierre-Yves Chibon b31474
            SESSION, project_id=repo.id, status=status)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon b31474
    return flask.render_template(
Pierre-Yves Chibon b31474
        'requests.html',
Pierre-Yves Chibon b31474
        select='requests',
Pierre-Yves Chibon b31474
        repo=repo,
Pierre-Yves Chibon b31474
        username=username,
Pierre-Yves Chibon b31474
        requests=requests,
Pierre-Yves Chibon b31474
        status=status,
Pierre-Yves Chibon b31474
    )
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon b31474
@APP.route('/<repo>/request-pull/<requestid>')</requestid></repo>
Pierre-Yves Chibon b31474
@APP.route('/fork/<username>/<repo>/request-pull/<requestid>')</requestid></repo></username>
Pierre-Yves Chibon b31474
def request_pull(repo, requestid, username=None):
Pierre-Yves Chibon 47950c
    """ Request pulling the changes from the fork into the project.
Pierre-Yves Chibon 47950c
    """
Pierre-Yves Chibon b31474
Pierre-Yves Chibon 47950c
    repo = progit.lib.get_project(SESSION, repo, user=username)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
    if not repo:
Pierre-Yves Chibon 47950c
        flask.abort(404, 'Project not found')
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
    request = progit.lib.get_pull_request(
Pierre-Yves Chibon 47950c
        SESSION, project_id=repo.id, requestid=requestid)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
    if not request:
Pierre-Yves Chibon 47950c
        flask.abort(404, 'Pull-request not found')
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 0784c9
    repo_from = request.repo_from
Pierre-Yves Chibon 17036c
Pierre-Yves Chibon 0784c9
    if repo_from.is_fork:
Pierre-Yves Chibon 0784c9
        repopath = os.path.join(APP.config['FORK_FOLDER'], repo_from.path)
Pierre-Yves Chibon 2c9e9f
    else:
Pierre-Yves Chibon 0784c9
        repopath = os.path.join(APP.config['GIT_FOLDER'], repo_from.path)
Pierre-Yves Chibon 47950c
    repo_obj = pygit2.Repository(repopath)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 0784c9
    if repo_from.parent:
Pierre-Yves Chibon 0784c9
        parentname = os.path.join(APP.config['GIT_FOLDER'], repo_from.parent.path)
Pierre-Yves Chibon 2c9e9f
    else:
Pierre-Yves Chibon 0784c9
        parentname = os.path.join(APP.config['GIT_FOLDER'], repo_from.path)
Pierre-Yves Chibon 47950c
    orig_repo = pygit2.Repository(parentname)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
    diff_commits = []
Pierre-Yves Chibon 47950c
    diffs = []
Pierre-Yves Chibon 47950c
    repo_commit = repo_obj[request.stop_id]
Pierre-Yves Chibon 47950c
    if not repo_obj.is_empty and not orig_repo.is_empty:
Pierre-Yves Chibon 2c9e9f
        orig_commit = orig_repo[
Pierre-Yves Chibon 2c9e9f
            orig_repo.lookup_branch('master').get_object().hex]
Pierre-Yves Chibon 2c9e9f
Pierre-Yves Chibon 2c9e9f
        master_commits = [
Pierre-Yves Chibon 2c9e9f
            commit.oid.hex
Pierre-Yves Chibon 2c9e9f
            for commit in orig_repo.walk(
Pierre-Yves Chibon 2c9e9f
                orig_repo.lookup_branch('master').get_object().hex,
Pierre-Yves Chibon 2c9e9f
                pygit2.GIT_SORT_TIME)
Pierre-Yves Chibon 2c9e9f
        ]
Pierre-Yves Chibon 2c9e9f
Pierre-Yves Chibon 2c9e9f
        for commit in repo_obj.walk(
Pierre-Yves Chibon 2c9e9f
                request.stop_id, pygit2.GIT_SORT_TIME):
Pierre-Yves Chibon 284eeb
            if request.status is False and commit.oid.hex == request.start_id:
Pierre-Yves Chibon 284eeb
                break
Pierre-Yves Chibon 284eeb
            elif request.status and commit.oid.hex in master_commits:
Pierre-Yves Chibon 47950c
                break
Pierre-Yves Chibon 47950c
            diff_commits.append(commit)
Pierre-Yves Chibon 47950c
            diffs.append(
Pierre-Yves Chibon 47950c
                repo_obj.diff(
Pierre-Yves Chibon 47950c
                    repo_obj.revparse_single(commit.parents[0].oid.hex),
Pierre-Yves Chibon 47950c
                    repo_obj.revparse_single(commit.oid.hex)
Pierre-Yves Chibon 47950c
                )
Pierre-Yves Chibon 47950c
            )
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
    elif orig_repo.is_empty:
Pierre-Yves Chibon 47950c
        orig_commit = None
Pierre-Yves Chibon 47950c
        diff = repo_commit.tree.diff_to_tree(swap=True)
Pierre-Yves Chibon 47950c
    else:
Pierre-Yves Chibon 47950c
        flask.flash(
Pierre-Yves Chibon 47950c
            'Fork is empty, there are no commits to request pulling',
Pierre-Yves Chibon 47950c
            'error')
Pierre-Yves Chibon 47950c
        return flask.redirect(flask.url_for(
Pierre-Yves Chibon ea8735
            'view_repo', username=username, repo=repo.name))
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
    html_diffs = []
Pierre-Yves Chibon 47950c
    for diff in diffs:
Pierre-Yves Chibon 47950c
        html_diffs.append(
Pierre-Yves Chibon 47950c
            highlight(
Pierre-Yves Chibon 47950c
                diff.patch,
Pierre-Yves Chibon 47950c
                DiffLexer(),
Pierre-Yves Chibon 47950c
                HtmlFormatter(
Pierre-Yves Chibon 47950c
                    noclasses=True,
Pierre-Yves Chibon 47950c
                    style="tango",)
Pierre-Yves Chibon 47950c
            )
Pierre-Yves Chibon 47950c
        )
Pierre-Yves Chibon 47950c
Johan Cwiklinski fd18bd
    form = progit.forms.ConfirmationForm()
Johan Cwiklinski fd18bd
Pierre-Yves Chibon 47950c
    return flask.render_template(
Pierre-Yves Chibon 47950c
        'pull_request.html',
Pierre-Yves Chibon 47950c
        select='requests',
Pierre-Yves Chibon 04ed02
        requestid=requestid,
Pierre-Yves Chibon 47950c
        repo=repo,
Pierre-Yves Chibon 41558c
        username=username,
Pierre-Yves Chibon 47950c
        request=request,
Pierre-Yves Chibon 2e8ea2
        repo_admin=is_repo_admin(request.repo),
Pierre-Yves Chibon 47950c
        repo_obj=repo_obj,
Pierre-Yves Chibon 47950c
        orig_repo=orig_repo,
Pierre-Yves Chibon 47950c
        diff_commits=diff_commits,
Pierre-Yves Chibon 47950c
        diffs=diffs,
Pierre-Yves Chibon 47950c
        html_diffs=html_diffs,
Johan Cwiklinski fd18bd
        mergeform=form,
Pierre-Yves Chibon 47950c
    )
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon e7de33
@APP.route('/<repo>/request-pull/<requestid>.patch')</requestid></repo>
Pierre-Yves Chibon e7de33
@APP.route('/fork/<username>/<repo>/request-pull/<requestid>.patch')</requestid></repo></username>
Pierre-Yves Chibon e7de33
def request_pull_patch(repo, requestid, username=None):
Pierre-Yves Chibon e7de33
    """ Returns the commits from the specified pull-request as patches.
Pierre-Yves Chibon e7de33
    """
Pierre-Yves Chibon e7de33
Pierre-Yves Chibon e7de33
    repo = progit.lib.get_project(SESSION, repo, user=username)
Pierre-Yves Chibon e7de33
Pierre-Yves Chibon e7de33
    if not repo:
Pierre-Yves Chibon e7de33
        flask.abort(404, 'Project not found')
Pierre-Yves Chibon e7de33
Pierre-Yves Chibon e7de33
    request = progit.lib.get_pull_request(
Pierre-Yves Chibon e7de33
        SESSION, project_id=repo.id, requestid=requestid)
Pierre-Yves Chibon e7de33
Pierre-Yves Chibon e7de33
    if not request:
Pierre-Yves Chibon e7de33
        flask.abort(404, 'Pull-request not found')
Pierre-Yves Chibon e7de33
Pierre-Yves Chibon e7de33
    repo = request.repo_from
Pierre-Yves Chibon e7de33
Pierre-Yves Chibon e7de33
    if repo.is_fork:
Pierre-Yves Chibon e7de33
        repopath = os.path.join(APP.config['FORK_FOLDER'], repo.path)
Pierre-Yves Chibon e7de33
    else:
Pierre-Yves Chibon e7de33
        repopath = os.path.join(APP.config['GIT_FOLDER'], repo.path)
Pierre-Yves Chibon e7de33
    repo_obj = pygit2.Repository(repopath)
Pierre-Yves Chibon e7de33
Pierre-Yves Chibon e7de33
    if repo.parent:
Pierre-Yves Chibon e7de33
        parentname = os.path.join(APP.config['GIT_FOLDER'], repo.parent.path)
Pierre-Yves Chibon e7de33
    else:
Pierre-Yves Chibon e7de33
        parentname = os.path.join(APP.config['GIT_FOLDER'], repo.path)
Pierre-Yves Chibon e7de33
    orig_repo = pygit2.Repository(parentname)
Pierre-Yves Chibon e7de33
Pierre-Yves Chibon e7de33
    diff_commits = []
Pierre-Yves Chibon e7de33
    repo_commit = repo_obj[request.stop_id]
Pierre-Yves Chibon e7de33
    if not repo_obj.is_empty and not orig_repo.is_empty:
Pierre-Yves Chibon e7de33
        orig_commit = orig_repo[
Pierre-Yves Chibon e7de33
            orig_repo.lookup_branch('master').get_object().hex]
Pierre-Yves Chibon e7de33
Pierre-Yves Chibon e7de33
        master_commits = [
Pierre-Yves Chibon e7de33
            commit.oid.hex
Pierre-Yves Chibon e7de33
            for commit in orig_repo.walk(
Pierre-Yves Chibon e7de33
                orig_repo.lookup_branch('master').get_object().hex,
Pierre-Yves Chibon e7de33
                pygit2.GIT_SORT_TIME)
Pierre-Yves Chibon e7de33
        ]
Pierre-Yves Chibon e7de33
Pierre-Yves Chibon e7de33
        repo_commit = repo_obj[request.start_id]
Pierre-Yves Chibon e7de33
Pierre-Yves Chibon e7de33
        for commit in repo_obj.walk(
Pierre-Yves Chibon e7de33
                request.stop_id, pygit2.GIT_SORT_TIME):
Pierre-Yves Chibon e7de33
            if commit.oid.hex in master_commits:
Pierre-Yves Chibon e7de33
                break
Pierre-Yves Chibon e7de33
            diff_commits.append(commit)
Pierre-Yves Chibon e7de33
Pierre-Yves Chibon e7de33
    elif orig_repo.is_empty:
Pierre-Yves Chibon e7de33
        orig_commit = None
Pierre-Yves Chibon e7de33
        diff = repo_commit.tree.diff_to_tree(swap=True)
Pierre-Yves Chibon e7de33
    else:
Pierre-Yves Chibon e7de33
        flask.flash(
Pierre-Yves Chibon e7de33
            'Fork is empty, there are no commits to request pulling',
Pierre-Yves Chibon e7de33
            'error')
Pierre-Yves Chibon e7de33
        return flask.redirect(flask.url_for(
Pierre-Yves Chibon e7de33
            'view_repo', username=username, repo=repo.name))
Pierre-Yves Chibon e7de33
Pierre-Yves Chibon e7de33
    diff_commits.reverse()
Pierre-Yves Chibon e7de33
    patch = progit.lib.commit_to_patch(repo_obj, diff_commits)
Pierre-Yves Chibon e7de33
Pierre-Yves Chibon e7de33
    return flask.Response(patch, content_type="text/plain;charset=UTF-8")
Pierre-Yves Chibon e7de33
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon cb10a1
@APP.route('/<repo>/request-pull/<requestid>/comment/<commit>/<row>',</row></commit></requestid></repo>
Pierre-Yves Chibon cb10a1
           methods=('GET', 'POST'))
Pierre-Yves Chibon cb10a1
@APP.route('/fork/<username>/<repo>/request-pull/<requestid>/comment/'</requestid></repo></username>
Pierre-Yves Chibon cb10a1
           '<commit>/<row>', methods=('GET', 'POST'))</row></commit>
Pierre-Yves Chibon cb10a1
def pull_request_add_comment(repo, requestid, commit, row, username=None):
Pierre-Yves Chibon cb10a1
    """ Add a comment to a commit in a pull-request.
Pierre-Yves Chibon cb10a1
    """
Pierre-Yves Chibon cb10a1
    repo = progit.lib.get_project(SESSION, repo, user=username)
Pierre-Yves Chibon cb10a1
Pierre-Yves Chibon cb10a1
    if not repo:
Pierre-Yves Chibon cb10a1
        flask.abort(404, 'Project not found')
Pierre-Yves Chibon cb10a1
Pierre-Yves Chibon cb10a1
    request = progit.lib.get_pull_request(
Pierre-Yves Chibon cb10a1
        SESSION, project_id=repo.id, requestid=requestid)
Pierre-Yves Chibon cb10a1
    repo = request.repo_from
Pierre-Yves Chibon cb10a1
Pierre-Yves Chibon cb10a1
    if not request:
Pierre-Yves Chibon cb10a1
        flask.abort(404, 'Pull-request not found')
Pierre-Yves Chibon cb10a1
Pierre-Yves Chibon cb10a1
    form = progit.forms.AddPullRequestCommentForm()
Pierre-Yves Chibon cb10a1
    form.commit.data = commit
Pierre-Yves Chibon cb10a1
    form.requestid.data = requestid
Pierre-Yves Chibon cb10a1
    form.row.data = row
Pierre-Yves Chibon cb10a1
Pierre-Yves Chibon 248f5e
    if form.validate_on_submit():
Pierre-Yves Chibon 248f5e
        comment = form.comment.data
Pierre-Yves Chibon 248f5e
Pierre-Yves Chibon 248f5e
        try:
Pierre-Yves Chibon 248f5e
            message = progit.lib.add_pull_request_comment(
Pierre-Yves Chibon 248f5e
                SESSION,
Pierre-Yves Chibon 248f5e
                request=request,
Pierre-Yves Chibon 248f5e
                commit=commit,
Pierre-Yves Chibon 248f5e
                row=row,
Pierre-Yves Chibon 248f5e
                comment=comment,
Pierre-Yves Chibon 248f5e
                user=flask.g.fas_user.username,
Pierre-Yves Chibon 248f5e
            )
Pierre-Yves Chibon 248f5e
            SESSION.commit()
Pierre-Yves Chibon 248f5e
            flask.flash(message)
Pierre-Yves Chibon 248f5e
        except SQLAlchemyError, err:  # pragma: no cover
Pierre-Yves Chibon 248f5e
            SESSION.rollback()
Pierre-Yves Chibon 248f5e
            flask.flash(str(err), 'error')
Pierre-Yves Chibon 248f5e
Pierre-Yves Chibon 248f5e
        return flask.redirect(flask.url_for(
Pierre-Yves Chibon 248f5e
            'request_pull', username=username,
Pierre-Yves Chibon 248f5e
            repo=repo.name, requestid=requestid))
Pierre-Yves Chibon cb10a1
Pierre-Yves Chibon cb10a1
    return flask.render_template(
Pierre-Yves Chibon cb10a1
        'pull_request_comment.html',
Pierre-Yves Chibon cb10a1
        select='requests',
Pierre-Yves Chibon cb10a1
        requestid=requestid,
Pierre-Yves Chibon cb10a1
        repo=repo,
Pierre-Yves Chibon cb10a1
        username=username,
Pierre-Yves Chibon cb10a1
        commit=commit,
Pierre-Yves Chibon cb10a1
        row=row,
Pierre-Yves Chibon cb10a1
        form=form,
Pierre-Yves Chibon cb10a1
    )
Pierre-Yves Chibon cb10a1
Pierre-Yves Chibon cb10a1
Pierre-Yves Chibon cb10a1
Pierre-Yves Chibon b31474
@APP.route('/<repo>/request-pull/merge/<requestid>')</requestid></repo>
Pierre-Yves Chibon b31474
@APP.route('/fork/<username>/<repo>/request-pull/merge/<requestid>')</requestid></repo></username>
Pierre-Yves Chibon b31474
def merge_request_pull(repo, requestid, username=None):
Pierre-Yves Chibon b31474
    """ Request pulling the changes from the fork into the project.
Pierre-Yves Chibon 47950c
    """
Johan Cwiklinski fd18bd
Johan Cwiklinski fd18bd
    form = progit.forms.ConfirmationForm()
Pierre-Yves Chibon 15e950
    if not form.validate_on_submit():
Pierre-Yves Chibon 15e950
        flask.flash('Invalid input submitted', 'error')
Pierre-Yves Chibon 15e950
        return flask.redirect(flask.url_for('view_repo', repo=repo.name))
Johan Cwiklinski fd18bd
Pierre-Yves Chibon 47950c
    repo = progit.lib.get_project(SESSION, repo, user=username)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
    if not repo:
Pierre-Yves Chibon 47950c
        flask.abort(404, 'Project not found')
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon b31474
    request = progit.lib.get_pull_request(
Pierre-Yves Chibon b31474
        SESSION, project_id=repo.id, requestid=requestid)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon b31474
    if not request:
Pierre-Yves Chibon b31474
        flask.abort(404, 'Pull-request not found')
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 4f2df5
    if not is_repo_admin(repo):
Pierre-Yves Chibon 4f2df5
        flask.abort(
Pierre-Yves Chibon 4f2df5
            403,
Pierre-Yves Chibon 4f2df5
            'You are not allowed to merge pull-request for this project')
Pierre-Yves Chibon 4f2df5
Pierre-Yves Chibon b31474
    error_output = flask.url_for(
Pierre-Yves Chibon b31474
        'request_pull', repo=repo.name, requestid=requestid)
Pierre-Yves Chibon b31474
    if username:
Pierre-Yves Chibon b31474
        error_output = flask.url_for(
Pierre-Yves Chibon b31474
            'fork_request_pull',
Pierre-Yves Chibon b31474
            repo=repo.name,
Pierre-Yves Chibon b31474
            requestid=requestid,
Pierre-Yves Chibon b31474
            username=username)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon b31474
    # Get the fork
Pierre-Yves Chibon 2c9e9f
    if request.repo_from.is_fork:
Pierre-Yves Chibon 2c9e9f
        repopath = os.path.join(
Pierre-Yves Chibon 2c9e9f
            APP.config['FORK_FOLDER'], request.repo_from.path)
Pierre-Yves Chibon 2c9e9f
    else:
Pierre-Yves Chibon 2c9e9f
        repopath = os.path.join(
Pierre-Yves Chibon 2c9e9f
            APP.config['GIT_FOLDER'], request.repo_from.path)
Pierre-Yves Chibon b31474
    fork_obj = pygit2.Repository(repopath)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon b31474
    # Get the original repo
Pierre-Yves Chibon b31474
    parentpath = os.path.join(APP.config['GIT_FOLDER'], request.repo.path)
Pierre-Yves Chibon b31474
    orig_repo = pygit2.Repository(parentpath)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon b31474
    # Clone the original repo into a temp folder
Pierre-Yves Chibon b31474
    newpath = tempfile.mkdtemp()
Pierre-Yves Chibon b31474
    new_repo = pygit2.clone_repository(parentpath, newpath)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon b31474
    repo_commit = fork_obj[request.stop_id]
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon b31474
    ori_remote = new_repo.remotes[0]
Pierre-Yves Chibon b31474
    # Add the fork as remote repo
Pierre-Yves Chibon e5d251
    reponame = '%s_%s' % (request.user.user, repo.name)
Pierre-Yves Chibon b31474
    remote = new_repo.create_remote(reponame, repopath)
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon b31474
    # Fetch the commits
Pierre-Yves Chibon b31474
    remote.fetch()
Pierre-Yves Chibon b31474
Pierre-Yves Chibon b31474
    merge = new_repo.merge(repo_commit.oid)
Pierre-Yves Chibon b31474
    master_ref = new_repo.lookup_reference('HEAD').resolve()
Pierre-Yves Chibon b31474
Pierre-Yves Chibon 59b3a3
    refname = '%s:%s' % (master_ref.name, master_ref.name)
Pierre-Yves Chibon d5c14e
    if merge.is_uptodate:
Pierre-Yves Chibon d5c14e
        flask.flash('Nothing to do, changes were already merged', 'error')
Pierre-Yves Chibon d5c14e
        progit.lib.close_pull_request(SESSION, request)
Pierre-Yves Chibon d5c14e
        SESSION.commit()
Pierre-Yves Chibon d5c14e
        return flask.redirect(error_output)
Pierre-Yves Chibon d5c14e
    elif merge.is_fastforward:
Pierre-Yves Chibon b31474
        master_ref.target = merge.fastforward_oid
Pierre-Yves Chibon b31474
        ori_remote.push(refname)
Pierre-Yves Chibon b31474
        flask.flash('Changes merged!')
Pierre-Yves Chibon b31474
    else:
Pierre-Yves Chibon 59b3a3
        new_repo.index.write()
Pierre-Yves Chibon 2c9e9f
        try:
Pierre-Yves Chibon 2c9e9f
            tree = new_repo.index.write_tree()
Pierre-Yves Chibon 2c9e9f
        except pygit2.GitError:
Pierre-Yves Chibon be7090
            shutil.rmtree(newpath)
Pierre-Yves Chibon 2c9e9f
            flask.flash('Merge conflicts!', 'error')
Pierre-Yves Chibon 2c9e9f
            return flask.redirect(flask.url_for(
Pierre-Yves Chibon 2c9e9f
                'request_pull',
Pierre-Yves Chibon 2c9e9f
                repo=repo.name,
Pierre-Yves Chibon 2c9e9f
                username=username,
Pierre-Yves Chibon 2c9e9f
                requestid=requestid))
Pierre-Yves Chibon 59b3a3
        head = new_repo.lookup_reference('HEAD').get_object()
Pierre-Yves Chibon 59b3a3
        commit = new_repo[head.oid]
Pierre-Yves Chibon 59b3a3
        sha = new_repo.create_commit(
Pierre-Yves Chibon 59b3a3
            'refs/heads/master',
Pierre-Yves Chibon 59b3a3
            repo_commit.author,
Pierre-Yves Chibon 59b3a3
            repo_commit.committer,
Pierre-Yves Chibon 59b3a3
            'Merge #%s `%s`' % (request.id, request.title),
Pierre-Yves Chibon 59b3a3
            tree,
Pierre-Yves Chibon 59b3a3
            [head.hex, repo_commit.oid.hex])
Pierre-Yves Chibon 59b3a3
        ori_remote.push(refname)
Pierre-Yves Chibon 59b3a3
        flask.flash('Changes merged!')
Pierre-Yves Chibon b31474
Pierre-Yves Chibon b31474
    # Update status
Pierre-Yves Chibon b31474
    progit.lib.close_pull_request(SESSION, request)
Pierre-Yves Chibon b31474
    SESSION.commit()
Pierre-Yves Chibon be7090
    shutil.rmtree(newpath)
Pierre-Yves Chibon b31474
Pierre-Yves Chibon b31474
    return flask.redirect(flask.url_for('view_repo', repo=repo.name))
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
Johan Cwiklinski fd18bd
@APP.route('/<repo>/request-pull/cancel/<requestid>',</requestid></repo>
Johan Cwiklinski fd18bd
        methods=('GET', 'POST'))
Johan Cwiklinski fd18bd
@APP.route('/fork/<username>/<repo>/request-pull/cancel/<requestid>',</requestid></repo></username>
Johan Cwiklinski fd18bd
        methods=('GET', 'POST'))
Johan Cwiklinski 86d9c4
def cancel_request_pull(repo, requestid, username=None):
Johan Cwiklinski 86d9c4
    """ Cancel request pulling request.
Johan Cwiklinski 86d9c4
    """
Johan Cwiklinski fd18bd
Johan Cwiklinski fd18bd
    form = progit.forms.ConfirmationForm()
Pierre-Yves Chibon 15e950
    if form.validate_on_submit():
Johan Cwiklinski fd18bd
Pierre-Yves Chibon 15e950
        repo = progit.lib.get_project(SESSION, repo, user=username)
Johan Cwiklinski 86d9c4
Pierre-Yves Chibon 15e950
        if not repo:
Pierre-Yves Chibon 15e950
            flask.abort(404, 'Project not found')
Johan Cwiklinski 86d9c4
Pierre-Yves Chibon 15e950
        request = progit.lib.get_pull_request(
Pierre-Yves Chibon 15e950
            SESSION, project_id=repo.id, requestid=requestid)
Johan Cwiklinski 86d9c4
Pierre-Yves Chibon 15e950
        if not request:
Pierre-Yves Chibon 15e950
            flask.abort(404, 'Pull-request not found')
Johan Cwiklinski 86d9c4
Pierre-Yves Chibon 15e950
        if not is_repo_admin(repo):
Pierre-Yves Chibon 15e950
            flask.abort(
Pierre-Yves Chibon 15e950
                403,
Pierre-Yves Chibon 15e950
                'You are not allowed to cancel pull-request for this project')
Johan Cwiklinski 86d9c4
Pierre-Yves Chibon 15e950
        progit.lib.close_pull_request(SESSION, request, merged=False)
Pierre-Yves Chibon 15e950
        SESSION.commit()
Pierre-Yves Chibon 15e950
        flask.flash('Request pull canceled!')
Pierre-Yves Chibon 15e950
    else:
Pierre-Yves Chibon 15e950
        flask.flash('Invalid input submitted', 'error')
Johan Cwiklinski 86d9c4
Johan Cwiklinski 86d9c4
    return flask.redirect(flask.url_for('view_repo', repo=repo.name))
Johan Cwiklinski 86d9c4
Johan Cwiklinski 86d9c4
Pierre-Yves Chibon 47950c
## Specific actions
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon 47950c
Pierre-Yves Chibon e03e84
@APP.route('/do_fork/<repo>')</repo>
Pierre-Yves Chibon e03e84
@APP.route('/do_fork/<username>/<repo>')</repo></username>
Pierre-Yves Chibon d5e3d0
@cla_required
Pierre-Yves Chibon 20ccbb
def fork_project(repo, username=None):
Pierre-Yves Chibon fac0b1
    """ Fork the project specified into the user's namespace
Pierre-Yves Chibon fac0b1
    """
Pierre-Yves Chibon 20ccbb
    repo = progit.lib.get_project(SESSION, repo, user=username)
Pierre-Yves Chibon 80bba3
Pierre-Yves Chibon 80bba3
    if repo is None:
Pierre-Yves Chibon fac0b1
        flask.abort(404)
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon 80bba3
    try:
Pierre-Yves Chibon 80bba3
        message = progit.lib.fork_project(
Pierre-Yves Chibon 80bba3
            session=SESSION,
Pierre-Yves Chibon 80bba3
            repo=repo,
Pierre-Yves Chibon e54062
            gitfolder=APP.config['GIT_FOLDER'],
Pierre-Yves Chibon 6160b6
            forkfolder=APP.config['FORK_FOLDER'],
Pierre-Yves Chibon 4b7a7d
            docfolder=APP.config['DOCS_FOLDER'],
Pierre-Yves Chibon a33978
            ticketfolder=APP.config['TICKETS_FOLDER'],
Pierre-Yves Chibon 80bba3
            user=flask.g.fas_user.username)
Pierre-Yves Chibon 80bba3
Pierre-Yves Chibon 80bba3
        SESSION.commit()
Pierre-Yves Chibon 59be40
        generate_gitolite_acls()
Pierre-Yves Chibon 80bba3
        flask.flash(message)
Pierre-Yves Chibon 80bba3
        return flask.redirect(
Pierre-Yves Chibon 792a86
            flask.url_for(
Pierre-Yves Chibon ea8735
                'view_repo',
Pierre-Yves Chibon 792a86
                username=flask.g.fas_user.username,
Pierre-Yves Chibon 792a86
                repo=repo.name)
Pierre-Yves Chibon 80bba3
        )
Pierre-Yves Chibon 80bba3
    except progit.exceptions.ProgitException, err:
Pierre-Yves Chibon 80bba3
        flask.flash(str(err), 'error')
Pierre-Yves Chibon 80bba3
    except SQLAlchemyError, err:  # pragma: no cover
Pierre-Yves Chibon 80bba3
        SESSION.rollback()
Pierre-Yves Chibon 80bba3
        flask.flash(str(err), 'error')
Pierre-Yves Chibon 80bba3
Pierre-Yves Chibon 792a86
    return flask.redirect(flask.url_for('view_repo', repo=repo.name))
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon fac0b1
Pierre-Yves Chibon 2c9e9f
@APP.route('/<repo>/request-pull/new',</repo>
Pierre-Yves Chibon 2c9e9f
           methods=('GET', 'POST'))
Pierre-Yves Chibon 2c9e9f
@APP.route('/<repo>/request-pull/new/<commitid>',</commitid></repo>
Pierre-Yves Chibon 2c9e9f
           methods=('GET', 'POST'))
Pierre-Yves Chibon ac8023
@APP.route('/fork/<username>/<repo>/request-pull/new',</repo></username>
Pierre-Yves Chibon 3bb858
           methods=('GET', 'POST'))
Pierre-Yves Chibon ac8023
@APP.route('/fork/<username>/<repo>/request-pull/new/<commitid>',</commitid></repo></username>
Pierre-Yves Chibon ac8023
           methods=('GET', 'POST'))
Pierre-Yves Chibon d5e3d0
@cla_required
Pierre-Yves Chibon 2c9e9f
def new_request_pull(repo, username=None, commitid=None):
Pierre-Yves Chibon 82055c
    """ Request pulling the changes from the fork into the project.
Pierre-Yves Chibon 82055c
    """
Pierre-Yves Chibon 82055c
    repo = progit.lib.get_project(SESSION, repo, user=username)
Pierre-Yves Chibon 82055c
Pierre-Yves Chibon 82055c
    if not repo:
Pierre-Yves Chibon 82055c
        flask.abort(404)
Pierre-Yves Chibon 82055c
Pierre-Yves Chibon 1e5fe9
    if not is_repo_admin(repo):
Pierre-Yves Chibon 1e5fe9
        flask.abort(
Pierre-Yves Chibon 1e5fe9
            403,
Pierre-Yves Chibon 1e5fe9
            'You are not allowed to create pull-requests for this project')
Pierre-Yves Chibon 1e5fe9
Pierre-Yves Chibon 2c9e9f
    if repo.is_fork:
Pierre-Yves Chibon 2c9e9f
        repopath = os.path.join(APP.config['FORK_FOLDER'], repo.path)
Pierre-Yves Chibon 2c9e9f
    else:
Pierre-Yves Chibon 2c9e9f
        repopath = os.path.join(APP.config['GIT_FOLDER'], repo.path)
Pierre-Yves Chibon 85755c
    repo_obj = pygit2.Repository(repopath)
Pierre-Yves Chibon 82055c
Pierre-Yves Chibon 2c9e9f
    if repo.parent:
Pierre-Yves Chibon 2c9e9f
        parentname = os.path.join(APP.config['GIT_FOLDER'], repo.parent.path)
Pierre-Yves Chibon 2c9e9f
    else:
Pierre-Yves Chibon 2c9e9f
        parentname = os.path.join(APP.config['GIT_FOLDER'], repo.path)
Pierre-Yves Chibon 82055c
    orig_repo = pygit2.Repository(parentname)
Pierre-Yves Chibon 82055c
Pierre-Yves Chibon 24542f
    frombranchname = flask.request.args.get('from_branch', 'master')
Pierre-Yves Chibon 24542f
    frombranch = repo_obj.lookup_branch(frombranchname)
Pierre-Yves Chibon 24542f
    if not frombranch:
Pierre-Yves Chibon 24542f
        flask.flash('Branch %s does not exist' % frombranchname, 'error')
Pierre-Yves Chibon 24542f
        frombranchname = 'master'
Pierre-Yves Chibon 24542f
Pierre-Yves Chibon efda13
    branchname = flask.request.args.get('branch', 'master')
Pierre-Yves Chibon efda13
    branch = orig_repo.lookup_branch(branchname)
Pierre-Yves Chibon efda13
    if not branch:
Pierre-Yves Chibon efda13
        flask.flash('Branch %s does not exist' % branchname, 'error')
Pierre-Yves Chibon efda13
        branchname = 'master'
Pierre-Yves Chibon efda13
Pierre-Yves Chibon 82055c
    if commitid is None:
Pierre-Yves Chibon 82055c
        commitid = repo_obj.head.target
Pierre-Yves Chibon e990b6
        if branchname:
Pierre-Yves Chibon 24542f
            branch = repo_obj.lookup_branch(frombranchname)
Pierre-Yves Chibon e990b6
            commitid = branch.get_object().hex
Pierre-Yves Chibon 82055c
Pierre-Yves Chibon 82055c
    diff_commits = []
Pierre-Yves Chibon 82055c
    diffs = []
Pierre-Yves Chibon 82055c
    if not repo_obj.is_empty and not orig_repo.is_empty:
Pierre-Yves Chibon 2c9e9f
        orig_commit = orig_repo[
Pierre-Yves Chibon efda13
            orig_repo.lookup_branch(branchname).get_object().hex]
Pierre-Yves Chibon 2c9e9f
Pierre-Yves Chibon 2c9e9f
        master_commits = [
Pierre-Yves Chibon 2c9e9f
            commit.oid.hex
Pierre-Yves Chibon 2c9e9f
            for commit in orig_repo.walk(
Pierre-Yves Chibon efda13
                orig_repo.lookup_branch(branchname).get_object().hex,
Pierre-Yves Chibon 2c9e9f
                pygit2.GIT_SORT_TIME)
Pierre-Yves Chibon 2c9e9f
        ]
Pierre-Yves Chibon 2c9e9f
Pierre-Yves Chibon 82055c
        repo_commit = repo_obj[commitid]
Pierre-Yves Chibon 82055c
Pierre-Yves Chibon 2c9e9f
        for commit in repo_obj.walk(
Pierre-Yves Chibon 2c9e9f
                repo_commit.oid.hex, pygit2.GIT_SORT_TIME):
Pierre-Yves Chibon 2c9e9f
            if commit.oid.hex in master_commits:
Pierre-Yves Chibon 82055c
                break
Pierre-Yves Chibon 82055c
            diff_commits.append(commit)
Pierre-Yves Chibon 82055c
            diffs.append(
Pierre-Yves Chibon 82055c
                repo_obj.diff(
Pierre-Yves Chibon 82055c
                    repo_obj.revparse_single(commit.parents[0].oid.hex),
Pierre-Yves Chibon 82055c
                    repo_obj.revparse_single(commit.oid.hex)
Pierre-Yves Chibon 82055c
                )
Pierre-Yves Chibon 82055c
            )
Pierre-Yves Chibon 82055c
Pierre-Yves Chibon 82055c
    elif orig_repo.is_empty:
Pierre-Yves Chibon ac8023
        orig_commit = None
Pierre-Yves Chibon 85755c
        repo_commit = repo_obj[repo_obj.head.target]
Pierre-Yves Chibon 85755c
        diff = repo_commit.tree.diff_to_tree(swap=True)
Pierre-Yves Chibon 82055c
    else:
Pierre-Yves Chibon 82055c
        flask.flash(
Pierre-Yves Chibon 82055c
            'Fork is empty, there are no commits to request pulling',
Pierre-Yves Chibon 82055c
            'error')
Pierre-Yves Chibon 82055c
        return flask.redirect(flask.url_for(
Pierre-Yves Chibon ea8735
            'view_repo', username=username, repo=repo.name))
Pierre-Yves Chibon 82055c
Pierre-Yves Chibon 82055c
    html_diffs = []
Pierre-Yves Chibon 82055c
    for diff in diffs:
Pierre-Yves Chibon 82055c
        html_diffs.append(
Pierre-Yves Chibon 82055c
            highlight(
Pierre-Yves Chibon 82055c
                diff.patch,
Pierre-Yves Chibon 82055c
                DiffLexer(),
Pierre-Yves Chibon 82055c
                HtmlFormatter(
Pierre-Yves Chibon 82055c
                    noclasses=True,
Pierre-Yves Chibon 82055c
                    style="tango",)
Pierre-Yves Chibon 82055c
            )
Pierre-Yves Chibon 82055c
        )
Pierre-Yves Chibon 82055c
Pierre-Yves Chibon ac8023
    form = progit.forms.RequestPullForm()
Pierre-Yves Chibon ac8023
    if form.validate_on_submit():
Pierre-Yves Chibon ac8023
        try:
Pierre-Yves Chibon ac8023
            if orig_commit:
Pierre-Yves Chibon ac8023
                orig_commit = orig_commit.oid.hex
Pierre-Yves Chibon 2c9e9f
Pierre-Yves Chibon 2c9e9f
            parent = repo
Pierre-Yves Chibon 2c9e9f
            if repo.parent:
Pierre-Yves Chibon 2c9e9f
                parent = repo.parent
Pierre-Yves Chibon 2c9e9f
Pierre-Yves Chibon ac8023
            message = progit.lib.new_pull_request(
Pierre-Yves Chibon ac8023
                SESSION,
Pierre-Yves Chibon 2c9e9f
                repo=parent,
Pierre-Yves Chibon ac8023
                repo_from=repo,
Pierre-Yves Chibon efda13
                branch=branchname,
Pierre-Yves Chibon ac8023
                title=form.title.data,
Pierre-Yves Chibon ac8023
                start_id=orig_commit,
Pierre-Yves Chibon ac8023
                stop_id=repo_commit.oid.hex,
Pierre-Yves Chibon ac8023
                user=flask.g.fas_user.username,
Pierre-Yves Chibon ac8023
            )
Pierre-Yves Chibon ac8023
            SESSION.commit()
Pierre-Yves Chibon ac8023
            flask.flash(message)
Pierre-Yves Chibon 8399d1
Pierre-Yves Chibon 2c9e9f
            if not parent.is_fork:
Pierre-Yves Chibon 8399d1
                url = flask.url_for(
Pierre-Yves Chibon 2c9e9f
                    'request_pulls', username=None, repo=parent.name)
Pierre-Yves Chibon 8399d1
            else:
Pierre-Yves Chibon 8399d1
                url = flask.url_for(
Pierre-Yves Chibon 2c9e9f
                    'request_pulls', username=parent.user, repo=parent.name)
Pierre-Yves Chibon 8399d1
Pierre-Yves Chibon 8399d1
            return flask.redirect(url)
Pierre-Yves Chibon ac8023
        except progit.exceptions.ProgitException, err:
Pierre-Yves Chibon ac8023
            flask.flash(str(err), 'error')
Pierre-Yves Chibon ac8023
        except SQLAlchemyError, err:  # pragma: no cover
Pierre-Yves Chibon ac8023
            SESSION.rollback()
Pierre-Yves Chibon ac8023
            flask.flash(str(err), 'error')
Pierre-Yves Chibon ac8023
Pierre-Yves Chibon 82055c
    return flask.render_template(
Pierre-Yves Chibon 82055c
        'pull_request.html',
Pierre-Yves Chibon 0c4c0b
        select='requests',
Pierre-Yves Chibon 82055c
        repo=repo,
Pierre-Yves Chibon 82055c
        username=username,
Pierre-Yves Chibon ac8023
        commitid=commitid,
Pierre-Yves Chibon 82055c
        repo_obj=repo_obj,
Pierre-Yves Chibon 82055c
        orig_repo=orig_repo,
Pierre-Yves Chibon 82055c
        diff_commits=diff_commits,
Pierre-Yves Chibon 82055c
        diffs=diffs,
Pierre-Yves Chibon 82055c
        html_diffs=html_diffs,
Pierre-Yves Chibon ac8023
        form=form,
Pierre-Yves Chibon 658f7c
        branches=[
Pierre-Yves Chibon 658f7c
            branch.replace('refs/heads/', '')
Pierre-Yves Chibon 658f7c
            for branch in sorted(orig_repo.listall_references())
Pierre-Yves Chibon 658f7c
        ],
Pierre-Yves Chibon 658f7c
        branchname=branchname,
Pierre-Yves Chibon 82055c
    )