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: head = new_repo.lookup_reference('HEAD').get_object() 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) commit = repo_commit.oid.hex else: tree = new_repo.index.write_tree() user_obj = pagure.lib.get_user(session, username) author = pygit2.Signature( user_obj.fullname.encode('utf-8'), user_obj.default_email.encode('utf-8')) commit = 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) fork_obj.run_hook(head.hex, commit, 'refs/heads/%s' % request.branch, username) 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')) commit = 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) fork_obj.run_hook(head.hex, commit, 'refs/heads/%s' % request.branch, username) 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!'
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: head = new_repo.lookup_reference('HEAD').get_object() 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) commit = repo_commit.oid.hex else: tree = new_repo.index.write_tree() user_obj = pagure.lib.get_user(session, username) author = pygit2.Signature( user_obj.fullname.encode('utf-8'), user_obj.default_email.encode('utf-8')) commit = 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) fork_obj.run_hook( head.hex, commit, 'refs/heads/%s' % request.branch, username) 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')) commit = 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) fork_obj.run_hook( head.hex, commit, 'refs/heads/%s' % request.branch, username) 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!'