From acd3eb0fc263ab10ad2168b1caa6dfafb6e851e5 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jun 22 2017 10:50:54 +0000 Subject: [PATCH 1/3] Create references for pull-request in the git repo for local checkout This will allow checking out locally a PR opened against a given project without having to add and manage a new remote. One note: while existing clone will not get the objects coming from the fork, new clones will download them, so git show will find the commits coming from forks even though they are not merged and even if the user did not fetch the corresponding pull/ references. Fixes https://pagure.io/pagure/issue/443 Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/lib/git.py b/pagure/lib/git.py index c50c27f..7f0a207 100644 --- a/pagure/lib/git.py +++ b/pagure/lib/git.py @@ -27,6 +27,8 @@ import pygit2 import werkzeug from sqlalchemy.exc import SQLAlchemyError +from pygit2.remote import RemoteCollection + import pagure import pagure.exceptions import pagure.lib @@ -1600,6 +1602,14 @@ def diff_pull_request( request.commit_stop = diff_commits[0].oid.hex session.add(request) session.commit() + + tasks.sync_pull_ref.delay( + request.project.name, + request.project.namespace, + request.project.user.username if request.project.is_fork else None, + request.id + ) + if commenttext: pagure.lib.add_pull_request_comment( session, request, @@ -1620,6 +1630,28 @@ def diff_pull_request( return diff_commits +def update_pull_ref(request, repo): + """ Create or update the refs/pull/ reference in the git repo. + """ + + repopath = pagure.get_repo_path(request.project) + reponame = '%s_%s' % (request.user.user, request.uid) + + _log.info( + ' Adding remote: %s pointing to: %s', reponame, repopath) + rc = RemoteCollection(repo) + remote = rc.create(reponame, repopath) + try: + _log.info( + ' Pushing refs/heads/%s to refs/pull/%s', + request.branch_from, request.id) + refname = 'refs/heads/%s:refs/pull/%s/head' % ( + request.branch_from, request.id) + PagureRepo.push(remote, refname) + finally: + rc.delete(reponame) + + def get_git_tags(project): """ Returns the list of tags created in the git repositorie of the specified project. diff --git a/pagure/lib/tasks.py b/pagure/lib/tasks.py index 411dee0..ed713da 100644 --- a/pagure/lib/tasks.py +++ b/pagure/lib/tasks.py @@ -417,3 +417,36 @@ def project_dowait(name, namespace, user): gc_clean() return ret('view_repo', repo=name, username=user, namespace=namespace) + + +@conn.task +def sync_pull_ref(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) + + 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() From 8b725c7761ec3439e26bbb8dec1218916efdb1e6 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jun 22 2017 10:50:54 +0000 Subject: [PATCH 2/3] Add unit-test checking that the ref is created when viewing a PR Signed-off-by: Pierre-Yves Chibon --- diff --git a/tests/test_pagure_flask_ui_fork.py b/tests/test_pagure_flask_ui_fork.py index 94dc1bc..fd631df 100644 --- a/tests/test_pagure_flask_ui_fork.py +++ b/tests/test_pagure_flask_ui_fork.py @@ -16,6 +16,7 @@ import unittest import shutil import sys import tempfile +import time import os import pygit2 @@ -216,6 +217,32 @@ class PagureFlaskForktests(tests.Modeltests): shutil.rmtree(newpath) + def test_request_pull_reference(self): + """ Test if there is a reference created for a new PR. """ + + tests.create_projects(self.session) + tests.create_projects_git( + os.path.join(self.path, 'requests'), bare=True) + + self.set_up_git_repo(new_project=None, branch_from='feature') + + project = pagure.get_authorized_project(self.session, 'test') + self.assertEqual(len(project.requests), 1) + + # View the pull-request + output = self.app.get('/test/pull-request/1') + self.assertEqual(output.status_code, 200) + + # Give time to the worker to process the task + time.sleep(1) + + gitrepo = os.path.join(self.path, 'repos', 'test.git') + repo = pygit2.Repository(gitrepo) + self.assertEqual( + list(repo.listall_references()), + ['refs/heads/feature', 'refs/heads/master', 'refs/pull/1/head'] + ) + @patch('pagure.lib.notify.send_email') def test_request_pull(self, send_email): """ Test the request_pull endpoint. """ From d972d957608fe34f3b5b2e18c5b54e38d4919862 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jun 22 2017 10:50:54 +0000 Subject: [PATCH 3/3] Add small docstring explaining why the test raises an exception Signed-off-by: Pierre-Yves Chibon --- diff --git a/tests/test_pagure_lib_git.py b/tests/test_pagure_lib_git.py index 604cc7f..3764e62 100644 --- a/tests/test_pagure_lib_git.py +++ b/tests/test_pagure_lib_git.py @@ -2956,6 +2956,7 @@ index 0000000..60f7480 self.assertEqual(req.id, 1) self.assertEqual(req.title, 'test PR') + # `master` branch not found self.assertRaises( pagure.exceptions.PagureException, pagure.lib.git.merge_pull_request,