Esempio n. 1
0
def sync_pull_ref(self, name, namespace, user, requestid):
    """ Synchronize a pull/ reference from the content in the forked repo,
    allowing local checkout of the pull-request.
    """

    session = pagure.lib.create_session()

    project = pagure.lib._get_project(
        session, namespace=namespace, name=name, user=user,
        case=APP.config.get('CASE_SENSITIVE', False))

    with project.lock('WORKER'):
        request = pagure.lib.search_pull_requests(
            session, project_id=project.id, requestid=requestid)
        _log.debug(
            'Update pull refs of: %s#%s',
            request.project.fullname, request.id)

        if request.remote:
            # Get the fork
            repopath = pagure.get_remote_repo_path(
                request.remote_git, request.branch_from)
        else:
            # Get the fork
            repopath = pagure.get_repo_path(request.project_from)
        _log.debug('   working on the repo in: %s', repopath)

        repo_obj = pygit2.Repository(repopath)
        pagure.lib.git.update_pull_ref(request, repo_obj)

    session.remove()
    gc_clean()
Esempio n. 2
0
def refresh_remote_pr(self, name, namespace, user, requestid):
    """ Refresh the local clone of a git repository used in a remote
    pull-request.
    """

    session = pagure.lib.create_session()

    project = pagure.lib._get_project(
        session, namespace=namespace, name=name, user=user,
        case=APP.config.get('CASE_SENSITIVE', False))

    request = pagure.lib.search_pull_requests(
        session, project_id=project.id, requestid=requestid)
    _log.debug(
        'refreshing remote pull-request: %s/#%s', request.project.fullname,
        request.id)

    clonepath = pagure.get_remote_repo_path(request.remote_git,
                                            request.branch_from)

    repo = pagure.lib.repo.PagureRepo(clonepath)
    repo.pull(branch=request.branch_from, force=True)

    refresh_pr_cache.delay(name, namespace, user)
    session.remove()
    del repo
    gc_clean()
    return ret('request_pull', username=user, namespace=namespace, repo=name,
               requestid=requestid)
Esempio n. 3
0
def pull_remote_repo(remote_git, branch_from):
    clonepath = pagure.get_remote_repo_path(remote_git,
                                            branch_from,
                                            ignore_non_exist=True)
    repo = pygit2.clone_repository(remote_git,
                                   clonepath,
                                   checkout_branch=branch_from)

    del repo
    gc_clean()
    return clonepath
Esempio n. 4
0
def pull_remote_repo(self, remote_git, branch_from):
    """ Clone a remote git repository locally for remote PRs.
    """

    clonepath = pagure.get_remote_repo_path(remote_git, branch_from,
                                            ignore_non_exist=True)
    repo = pygit2.clone_repository(
        remote_git, clonepath, checkout_branch=branch_from)

    del repo
    gc_clean()
    return clonepath
Esempio n. 5
0
File: git.py Progetto: 0-T-0/pagure
def merge_pull_request(
        session, request, username, request_folder, domerge=True):
    ''' Merge the specified pull-request.
    '''
    if request.remote:
        # Get the fork
        repopath = pagure.get_remote_repo_path(
            request.remote_git, request.branch_from)
    else:
        # Get the fork
        repopath = pagure.get_repo_path(request.project_from)

    fork_obj = PagureRepo(repopath)

    # Get the original repo
    parentpath = pagure.get_repo_path(request.project)

    # Clone the original repo into a temp folder
    newpath = tempfile.mkdtemp(prefix='pagure-pr-merge')
    new_repo = pygit2.clone_repository(parentpath, newpath)

    # Update the start and stop commits in the DB, one last time
    diff_commits = diff_pull_request(
        session, request, fork_obj, PagureRepo(parentpath),
        requestfolder=request_folder, with_diff=False)[0]

    if request.project.settings.get(
            'Enforce_signed-off_commits_in_pull-request', False):
        for commit in diff_commits:
            if 'signed-off-by' not in commit.message.lower():
                shutil.rmtree(newpath)
                raise pagure.exceptions.PagureException(
                    'This repo enforces that all commits are '
                    'signed off by their author. ')

    # Checkout the correct branch
    branch_ref = get_branch_ref(new_repo, request.branch)
    if not branch_ref:
        shutil.rmtree(newpath)
        raise pagure.exceptions.BranchNotFoundException(
            'Branch %s could not be found in the repo %s' % (
                request.branch, request.project.fullname
            ))

    new_repo.checkout(branch_ref)

    branch = get_branch_ref(fork_obj, request.branch_from)
    if not branch:
        shutil.rmtree(newpath)
        raise pagure.exceptions.BranchNotFoundException(
            'Branch %s could not be found in the repo %s' % (
                request.branch_from, request.project_from.fullname
                if request.project_from else request.remote_git
            ))

    repo_commit = fork_obj[branch.get_object().hex]

    ori_remote = new_repo.remotes[0]
    # Add the fork as remote repo
    reponame = '%s_%s' % (request.user.user, request.uid)

    remote = new_repo.create_remote(reponame, repopath)

    # Fetch the commits
    remote.fetch()

    merge = new_repo.merge(repo_commit.oid)
    if merge is None:
        mergecode = new_repo.merge_analysis(repo_commit.oid)[0]

    refname = '%s:refs/heads/%s' % (branch_ref.name, request.branch)
    if (
            (merge is not None and merge.is_uptodate)
            or
            (merge is None and
             mergecode & pygit2.GIT_MERGE_ANALYSIS_UP_TO_DATE)):

        if domerge:
            pagure.lib.close_pull_request(
                session, request, username,
                requestfolder=request_folder)
            shutil.rmtree(newpath)
            try:
                session.commit()
            except SQLAlchemyError as err:  # pragma: no cover
                session.rollback()
                pagure.APP.logger.exception(err)
                raise pagure.exceptions.PagureException(
                    'Could not close this pull-request')
            raise pagure.exceptions.PagureException(
                'Nothing to do, changes were already merged')
        else:
            request.merge_status = 'NO_CHANGE'
            session.commit()
            shutil.rmtree(newpath)
            return 'NO_CHANGE'

    elif (
            (merge is not None and merge.is_fastforward)
            or
            (merge is None and
             mergecode & pygit2.GIT_MERGE_ANALYSIS_FASTFORWARD)):

        if domerge:
            if not request.project.settings.get('always_merge', False):
                if merge is not None:
                    # This is depending on the pygit2 version
                    branch_ref.target = merge.fastforward_oid
                elif merge is None and mergecode is not None:
                    branch_ref.set_target(repo_commit.oid.hex)
            else:
                tree = new_repo.index.write_tree()
                head = new_repo.lookup_reference('HEAD').get_object()
                user_obj = pagure.lib.__get_user(session, username)
                author = pygit2.Signature(
                    user_obj.fullname.encode('utf-8'),
                    user_obj.default_email.encode('utf-8'))
                new_repo.create_commit(
                    'refs/heads/%s' % request.branch,
                    author,
                    author,
                    'Merge #%s `%s`' % (request.id, request.title),
                    tree,
                    [head.hex, repo_commit.oid.hex])

            PagureRepo.push(ori_remote, refname)
        else:
            request.merge_status = 'FFORWARD'
            session.commit()
            shutil.rmtree(newpath)
            return 'FFORWARD'

    else:
        tree = None
        try:
            tree = new_repo.index.write_tree()
        except pygit2.GitError:
            shutil.rmtree(newpath)
            if domerge:
                raise pagure.exceptions.PagureException('Merge conflicts!')
            else:
                request.merge_status = 'CONFLICTS'
                session.commit()
                return 'CONFLICTS'

        if domerge:
            head = new_repo.lookup_reference('HEAD').get_object()
            user_obj = pagure.lib.__get_user(session, username)
            author = pygit2.Signature(
                user_obj.fullname.encode('utf-8'),
                user_obj.default_email.encode('utf-8'))
            new_repo.create_commit(
                'refs/heads/%s' % request.branch,
                author,
                author,
                'Merge #%s `%s`' % (request.id, request.title),
                tree,
                [head.hex, repo_commit.oid.hex])
            PagureRepo.push(ori_remote, refname)

        else:
            request.merge_status = 'MERGE'
            session.commit()
            shutil.rmtree(newpath)
            return 'MERGE'


    # Update status
    pagure.lib.close_pull_request(
        session, request, username,
        requestfolder=request_folder,
    )
    try:
        # Reset the merge_status of all opened PR to refresh their cache
        pagure.lib.reset_status_pull_request(session, request.project)
        session.commit()
    except SQLAlchemyError as err:  # pragma: no cover
        session.rollback()
        pagure.APP.logger.exception(err)
        shutil.rmtree(newpath)
        raise pagure.exceptions.PagureException(
            'Could not update this pull-request in the database')
    shutil.rmtree(newpath)

    return 'Changes merged!'
Esempio n. 6
0
def new_remote_request_pull(repo, username=None):
    """ Request pulling the changes from a remote fork into the project.
    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)
    confirm = flask.request.values.get('confirm', False)

    if not repo:
        flask.abort(404)

    if not repo.settings.get('pull_requests', True):
        flask.abort(404, 'No pull-requests found for this project')

    parentpath = pagure.get_repo_path(repo)
    orig_repo = pygit2.Repository(parentpath)

    repo_admin = is_repo_admin(repo)

    form = pagure.forms.RemoteRequestPullForm()
    if form.validate_on_submit():
        branch_from = form.branch_from.data.strip()
        branch_to = form.branch_to.data.strip()
        remote_git = form.git_repo.data.strip()

        repopath = pagure.get_remote_repo_path(remote_git, branch_from)
        repo_obj = pygit2.Repository(repopath)

        try:
            diff, diff_commits, orig_commit = _get_pr_info(
                repo_obj, orig_repo, branch_from, branch_to)
        except pagure.exceptions.PagureException as err:
            flask.flash(err.message, 'error')
            return flask.redirect(flask.url_for(
                'view_repo', username=username, repo=repo.name))

        if not confirm:
            return flask.render_template(
                'pull_request.html',
                select='requests',
                repo=repo,
                username=username,
                repo_obj=repo_obj,
                orig_repo=orig_repo,
                diff_commits=diff_commits,
                diff=diff,
                form=form,
                branches=sorted(orig_repo.listall_branches()),
                branch_to=branch_to,
                branch_from=branch_from,
                repo_admin=repo_admin,
                remote_git=remote_git,
            )

        try:
            if repo.settings.get(
                    'Enforce_signed-off_commits_in_pull-request', False):
                for commit in diff_commits:
                    if 'signed-off-by' not in commit.message.lower():
                        raise pagure.exceptions.PagureException(
                            'This repo enforces that all commits are '
                            'signed off by their author. ')

            if orig_commit:
                orig_commit = orig_commit.oid.hex

            parent = repo
            if repo.parent:
                parent = repo.parent

            request = pagure.lib.new_pull_request(
                SESSION,
                repo_to=parent,
                branch_to=branch_to,
                branch_from=branch_from,
                repo_from=None,
                remote_git=remote_git,
                title=form.title.data,
                user=flask.g.fas_user.username,
                requestfolder=APP.config['REQUESTS_FOLDER'],
            )
            try:
                SESSION.commit()
                flask.flash('Request created')
            except SQLAlchemyError as err:  # pragma: no cover
                SESSION.rollback()
                APP.logger.exception(err)
                flask.flash(
                    'Could not register this pull-request in '
                    'the database', 'error')

            if not parent.is_fork:
                url = flask.url_for(
                    'request_pull', requestid=request.id,
                    username=None, repo=parent.name)
            else:
                url = flask.url_for(
                    'request_pull', requestid=request.id,
                    username=parent.user, repo=parent.name)

            return flask.redirect(url)
        except pagure.exceptions.PagureException, err:  # pragma: no cover
            # There could be a PagureException thrown if the
            # flask.g.fas_user wasn't in the DB but then it shouldn't
            # be recognized as a repo admin and thus, if we ever are
            # here, we are in trouble.
            flask.flash(str(err), 'error')
        except SQLAlchemyError, err:  # pragma: no cover
            SESSION.rollback()
            flask.flash(str(err), 'error')
Esempio n. 7
0
def request_pull_patch(repo, requestid, username=None):
    """ Returns the commits from the specified pull-request as patches.
    """

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not repo.settings.get('pull_requests', True):
        flask.abort(404, 'No pull-requests found for this project')

    request = pagure.lib.search_pull_requests(
        SESSION, project_id=repo.id, requestid=requestid)

    if not request:
        flask.abort(404, 'Pull-request not found')

    if request.remote:
        repopath = pagure.get_remote_repo_path(
            request.remote_git, request.branch_from)
        parentpath = pagure.get_repo_path(request.project)
    else:
        repo_from = request.project_from
        repopath = pagure.get_repo_path(repo_from)
        parentpath = _get_parent_repo_path(repo_from)

    repo_obj = pygit2.Repository(repopath)
    orig_repo = pygit2.Repository(parentpath)

    branch = repo_obj.lookup_branch(request.branch_from)
    commitid = None
    if branch:
        commitid = branch.get_object().hex

    diff_commits = []
    if request.status != 'Open':
        commitid = request.commit_stop
        for commit in repo_obj.walk(commitid, pygit2.GIT_SORT_TIME):
            diff_commits.append(commit)
            if commit.oid.hex == request.commit_start:
                break
    else:
        try:
            diff_commits = pagure.lib.git.diff_pull_request(
                SESSION, request, repo_obj, orig_repo,
                requestfolder=APP.config['REQUESTS_FOLDER'],
                with_diff=False)[0]
        except pagure.exceptions.PagureException as err:
            flask.flash(err.message, 'error')
            return flask.redirect(flask.url_for(
                'view_repo', username=username, repo=repo.name))
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            flask.flash(
                'Could not update this pull-request in the database',
                'error')

    diff_commits.reverse()
    patch = pagure.lib.git.commit_to_patch(repo_obj, diff_commits)

    return flask.Response(patch, content_type="text/plain;charset=UTF-8")
Esempio n. 8
0
def request_pull(repo, requestid, username=None):
    """ Request pulling the changes from the fork into the project.
    """

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not repo.settings.get('pull_requests', True):
        flask.abort(404, 'No pull-requests found for this project')

    request = pagure.lib.search_pull_requests(
        SESSION, project_id=repo.id, requestid=requestid)

    if not request:
        flask.abort(404, 'Pull-request not found')

    if request.remote:
        repopath = pagure.get_remote_repo_path(
            request.remote_git, request.branch_from)
        parentpath = pagure.get_repo_path(request.project)
    else:
        repo_from = request.project_from
        repopath = pagure.get_repo_path(repo_from)
        parentpath = _get_parent_repo_path(repo_from)

    repo_obj = pygit2.Repository(repopath)
    orig_repo = pygit2.Repository(parentpath)

    diff_commits = []
    diff = None
    # Closed pull-request
    if request.status != 'Open':
        commitid = request.commit_stop
        try:
            for commit in repo_obj.walk(commitid, pygit2.GIT_SORT_TIME):
                diff_commits.append(commit)
                if commit.oid.hex == request.commit_start:
                    break
        except KeyError:
            # This happens when repo.walk() cannot find commitid
            pass

        if diff_commits:
            diff = repo_obj.diff(
                repo_obj.revparse_single(diff_commits[-1].parents[0].oid.hex),
                repo_obj.revparse_single(diff_commits[0].oid.hex)
            )
    else:
        try:
            diff_commits, diff = pagure.lib.git.diff_pull_request(
                SESSION, request, repo_obj, orig_repo,
                requestfolder=APP.config['REQUESTS_FOLDER'])
        except pagure.exceptions.PagureException as err:
            flask.flash(err.message, 'error')
            return flask.redirect(flask.url_for(
                'view_repo', username=username, repo=repo.name))
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            flask.flash(
                'Could not update this pull-request in the database',
                'error')

    if diff:
        diff.find_similar()

    form = pagure.forms.ConfirmationForm()

    return flask.render_template(
        'pull_request.html',
        select='requests',
        requestid=requestid,
        repo=repo,
        username=username,
        pull_request=request,
        repo_admin=is_repo_admin(request.project),
        diff_commits=diff_commits,
        diff=diff,
        mergeform=form,
    )
Esempio n. 9
0
def new_remote_request_pull(repo, username=None):
    """ Request pulling the changes from a remote fork into the project.
    """
    repo = pagure.lib.get_project(SESSION, repo, user=username)
    confirm = flask.request.values.get('confirm', False)

    if not repo:
        flask.abort(404)

    if not repo.settings.get('pull_requests', True):
        flask.abort(404, 'No pull-requests found for this project')

    parentpath = pagure.get_repo_path(repo)
    orig_repo = pygit2.Repository(parentpath)

    repo_admin = is_repo_admin(repo)

    form = pagure.forms.RemoteRequestPullForm()
    if form.validate_on_submit():
        branch_from = form.branch_from.data.strip()
        branch_to = form.branch_to.data.strip()
        remote_git = form.git_repo.data.strip()

        repopath = pagure.get_remote_repo_path(remote_git, branch_from)
        repo_obj = pygit2.Repository(repopath)

        try:
            diff, diff_commits, orig_commit = _get_pr_info(
                repo_obj, orig_repo, branch_from, branch_to)
        except pagure.exceptions.PagureException as err:
            flask.flash(err.message, 'error')
            return flask.redirect(
                flask.url_for('view_repo', username=username, repo=repo.name))

        if not confirm:
            return flask.render_template(
                'pull_request.html',
                select='requests',
                repo=repo,
                username=username,
                repo_obj=repo_obj,
                orig_repo=orig_repo,
                diff_commits=diff_commits,
                diff=diff,
                form=form,
                branches=sorted(orig_repo.listall_branches()),
                branch_to=branch_to,
                branch_from=branch_from,
                repo_admin=repo_admin,
                remote_git=remote_git,
            )

        try:
            if repo.settings.get('Enforce_signed-off_commits_in_pull-request',
                                 False):
                for commit in diff_commits:
                    if 'signed-off-by' not in commit.message.lower():
                        raise pagure.exceptions.PagureException(
                            'This repo enforces that all commits are '
                            'signed off by their author. ')

            if orig_commit:
                orig_commit = orig_commit.oid.hex

            parent = repo
            if repo.parent:
                parent = repo.parent

            request = pagure.lib.new_pull_request(
                SESSION,
                repo_to=parent,
                branch_to=branch_to,
                branch_from=branch_from,
                repo_from=None,
                remote_git=remote_git,
                title=form.title.data,
                user=flask.g.fas_user.username,
                requestfolder=APP.config['REQUESTS_FOLDER'],
            )
            try:
                SESSION.commit()
                flask.flash('Request created')
            except SQLAlchemyError as err:  # pragma: no cover
                SESSION.rollback()
                APP.logger.exception(err)
                flask.flash(
                    'Could not register this pull-request in '
                    'the database', 'error')

            if not parent.is_fork:
                url = flask.url_for('request_pull',
                                    requestid=request.id,
                                    username=None,
                                    repo=parent.name)
            else:
                url = flask.url_for('request_pull',
                                    requestid=request.id,
                                    username=parent.user,
                                    repo=parent.name)

            return flask.redirect(url)
        except pagure.exceptions.PagureException, err:  # pragma: no cover
            # There could be a PagureException thrown if the
            # flask.g.fas_user wasn't in the DB but then it shouldn't
            # be recognized as a repo admin and thus, if we ever are
            # here, we are in trouble.
            flask.flash(str(err), 'error')
        except SQLAlchemyError, err:  # pragma: no cover
            SESSION.rollback()
            flask.flash(str(err), 'error')
Esempio n. 10
0
def request_pull_patch(repo, requestid, username=None):
    """ Returns the commits from the specified pull-request as patches.
    """

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not repo.settings.get('pull_requests', True):
        flask.abort(404, 'No pull-requests found for this project')

    request = pagure.lib.search_pull_requests(SESSION,
                                              project_id=repo.id,
                                              requestid=requestid)

    if not request:
        flask.abort(404, 'Pull-request not found')

    if request.remote:
        repopath = pagure.get_remote_repo_path(request.remote_git,
                                               request.branch_from)
        parentpath = pagure.get_repo_path(request.project)
    else:
        repo_from = request.project_from
        repopath = pagure.get_repo_path(repo_from)
        parentpath = _get_parent_repo_path(repo_from)

    repo_obj = pygit2.Repository(repopath)
    orig_repo = pygit2.Repository(parentpath)

    branch = repo_obj.lookup_branch(request.branch_from)
    commitid = None
    if branch:
        commitid = branch.get_object().hex

    diff_commits = []
    if request.status != 'Open':
        commitid = request.commit_stop
        for commit in repo_obj.walk(commitid, pygit2.GIT_SORT_TIME):
            diff_commits.append(commit)
            if commit.oid.hex == request.commit_start:
                break
    else:
        try:
            diff_commits = pagure.lib.git.diff_pull_request(
                SESSION,
                request,
                repo_obj,
                orig_repo,
                requestfolder=APP.config['REQUESTS_FOLDER'],
                with_diff=False)[0]
        except pagure.exceptions.PagureException as err:
            flask.flash(err.message, 'error')
            return flask.redirect(
                flask.url_for('view_repo', username=username, repo=repo.name))
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            flask.flash('Could not update this pull-request in the database',
                        'error')

    diff_commits.reverse()
    patch = pagure.lib.git.commit_to_patch(repo_obj, diff_commits)

    return flask.Response(patch, content_type="text/plain;charset=UTF-8")
Esempio n. 11
0
def request_pull(repo, requestid, username=None):
    """ Request pulling the changes from the fork into the project.
    """

    repo = pagure.lib.get_project(SESSION, repo, user=username)

    if not repo:
        flask.abort(404, 'Project not found')

    if not repo.settings.get('pull_requests', True):
        flask.abort(404, 'No pull-requests found for this project')

    request = pagure.lib.search_pull_requests(SESSION,
                                              project_id=repo.id,
                                              requestid=requestid)

    if not request:
        flask.abort(404, 'Pull-request not found')

    if request.remote:
        repopath = pagure.get_remote_repo_path(request.remote_git,
                                               request.branch_from)
        parentpath = pagure.get_repo_path(request.project)
    else:
        repo_from = request.project_from
        repopath = pagure.get_repo_path(repo_from)
        parentpath = _get_parent_repo_path(repo_from)

    repo_obj = pygit2.Repository(repopath)
    orig_repo = pygit2.Repository(parentpath)

    diff_commits = []
    diff = None
    # Closed pull-request
    if request.status != 'Open':
        commitid = request.commit_stop
        try:
            for commit in repo_obj.walk(commitid, pygit2.GIT_SORT_TIME):
                diff_commits.append(commit)
                if commit.oid.hex == request.commit_start:
                    break
        except KeyError:
            # This happens when repo.walk() cannot find commitid
            pass

        if diff_commits:
            diff = repo_obj.diff(
                repo_obj.revparse_single(diff_commits[-1].parents[0].oid.hex),
                repo_obj.revparse_single(diff_commits[0].oid.hex))
    else:
        try:
            diff_commits, diff = pagure.lib.git.diff_pull_request(
                SESSION,
                request,
                repo_obj,
                orig_repo,
                requestfolder=APP.config['REQUESTS_FOLDER'])
        except pagure.exceptions.PagureException as err:
            flask.flash(err.message, 'error')
            return flask.redirect(
                flask.url_for('view_repo', username=username, repo=repo.name))
        except SQLAlchemyError as err:  # pragma: no cover
            SESSION.rollback()
            APP.logger.exception(err)
            flask.flash('Could not update this pull-request in the database',
                        'error')

    if diff:
        diff.find_similar()

    form = pagure.forms.ConfirmationForm()

    return flask.render_template(
        'pull_request.html',
        select='requests',
        requestid=requestid,
        repo=repo,
        username=username,
        pull_request=request,
        repo_admin=is_repo_admin(request.project),
        diff_commits=diff_commits,
        diff=diff,
        mergeform=form,
    )
Esempio n. 12
0
def merge_pull_request(session,
                       request,
                       username,
                       request_folder,
                       domerge=True):
    ''' Merge the specified pull-request.
    '''
    if request.remote:
        # Get the fork
        repopath = pagure.get_remote_repo_path(request.remote_git,
                                               request.branch_from)
    else:
        # Get the fork
        repopath = pagure.get_repo_path(request.project_from)

    fork_obj = PagureRepo(repopath)

    # Get the original repo
    parentpath = pagure.get_repo_path(request.project)

    # Clone the original repo into a temp folder
    newpath = tempfile.mkdtemp(prefix='pagure-pr-merge')
    new_repo = pygit2.clone_repository(parentpath, newpath)

    # Update the start and stop commits in the DB, one last time
    diff_commits = diff_pull_request(session,
                                     request,
                                     PagureRepo(parentpath),
                                     fork_obj,
                                     requestfolder=request_folder,
                                     with_diff=False)[0]

    if request.project.settings.get(
            'Enforce_signed-off_commits_in_pull-request', False):
        for commit in diff_commits:
            if 'signed-off-by' not in commit.message.lower():
                raise pagure.exceptions.PagureException(
                    'This repo enforces that all commits are '
                    'signed off by their author. ')

    # Checkout the correct branch
    branch_ref = get_branch_ref(new_repo, request.branch)
    if not branch_ref:
        shutil.rmtree(newpath)
        raise pagure.exceptions.BranchNotFoundException(
            'Branch %s could not be found in the repo %s' %
            (request.branch, request.project.fullname))

    new_repo.checkout(branch_ref)

    branch = get_branch_ref(fork_obj, request.branch_from)
    if not branch:
        shutil.rmtree(newpath)
        raise pagure.exceptions.BranchNotFoundException(
            'Branch %s could not be found in the repo %s' %
            (request.branch_from, request.project_from.fullname
             if request.project_from else request.remote_git))

    repo_commit = fork_obj[branch.get_object().hex]

    ori_remote = new_repo.remotes[0]
    # Add the fork as remote repo
    reponame = '%s_%s' % (request.user.user, request.uid)

    remote = new_repo.create_remote(reponame, repopath)

    # Fetch the commits
    remote.fetch()

    merge = new_repo.merge(repo_commit.oid)
    if merge is None:
        mergecode = new_repo.merge_analysis(repo_commit.oid)[0]

    refname = '%s:refs/heads/%s' % (branch_ref.name, request.branch)
    if ((merge is not None and merge.is_uptodate) or
        (merge is None and mergecode & pygit2.GIT_MERGE_ANALYSIS_UP_TO_DATE)):

        if domerge:
            pagure.lib.close_pull_request(session,
                                          request,
                                          username,
                                          requestfolder=request_folder)
            try:
                session.commit()
            except SQLAlchemyError as err:  # pragma: no cover
                session.rollback()
                pagure.APP.logger.exception(err)
                shutil.rmtree(newpath)
                raise pagure.exceptions.PagureException(
                    'Could not close this pull-request')
            raise pagure.exceptions.PagureException(
                'Nothing to do, changes were already merged')
        else:
            request.merge_status = 'NO_CHANGE'
            session.commit()
            return 'NO_CHANGE'

    elif (
        (merge is not None and merge.is_fastforward) or
        (merge is None and mergecode & pygit2.GIT_MERGE_ANALYSIS_FASTFORWARD)):

        if domerge:
            if merge is not None:
                # This is depending on the pygit2 version
                branch_ref.target = merge.fastforward_oid
            elif merge is None and mergecode is not None:
                branch_ref.set_target(repo_commit.oid.hex)

            PagureRepo.push(ori_remote, refname)
        else:
            request.merge_status = 'FFORWARD'
            session.commit()
            return 'FFORWARD'

    else:
        tree = None
        try:
            tree = new_repo.index.write_tree()
        except pygit2.GitError:
            shutil.rmtree(newpath)
            if domerge:
                raise pagure.exceptions.PagureException('Merge conflicts!')
            else:
                request.merge_status = 'CONFLICTS'
                session.commit()
                return 'CONFLICTS'

        if not domerge:
            request.merge_status = 'MERGE'
            session.commit()
            return 'MERGE'

        head = new_repo.lookup_reference('HEAD').get_object()
        new_repo.create_commit('refs/heads/%s' % request.branch,
                               repo_commit.author, repo_commit.committer,
                               'Merge #%s `%s`' % (request.id, request.title),
                               tree, [head.hex, repo_commit.oid.hex])
        PagureRepo.push(ori_remote, refname)

    # Update status
    pagure.lib.close_pull_request(
        session,
        request,
        username,
        requestfolder=request_folder,
    )
    try:
        # Reset the merge_status of all opened PR to refresh their cache
        pagure.lib.reset_status_pull_request(session, request.project)
        session.commit()
    except SQLAlchemyError as err:  # pragma: no cover
        session.rollback()
        pagure.APP.logger.exception(err)
        shutil.rmtree(newpath)
        raise pagure.exceptions.PagureException(
            'Could not update this pull-request in the database')
    shutil.rmtree(newpath)

    return 'Changes merged!'