diff --git a/progit/__init__.py b/progit/__init__.py index 9c77608..900826a 100644 --- a/progit/__init__.py +++ b/progit/__init__.py @@ -183,5 +183,5 @@ def __get_file_in_tree(repo_obj, tree, filepath): ## Import the application -import progit.app import progit.fork +import progit.urls diff --git a/progit/app.py b/progit/app.py index fa58f9c..2bb8dab 100644 --- a/progit/app.py +++ b/progit/app.py @@ -27,176 +27,18 @@ from progit import APP, SESSION, LOG, __get_file_in_tree ### Application -@APP.route('/') -def index(): - """ Front page of the application. - """ - page = flask.request.args.get('page', 1) - try: - page = int(page) - except ValueError: - page = 1 - - limit = APP.config['ITEM_PER_PAGE'] - start = limit * (page - 1) - - repos = progit.lib.list_projects(SESSION, fork=False, start=start, limit=limit) - num_repos = progit.lib.list_projects(SESSION, fork=False, count=True) - - total_page = int(ceil(num_repos / float(limit))) - - return flask.render_template( - 'index.html', - repos=repos, - total_page=total_page, - page=page, - ) - - -@APP.route('/users/') -def view_users(): - """ Present the list of users. - """ - page = flask.request.args.get('page', 1) - try: - page = int(page) - except ValueError: - page = 1 - - ## TODO: retrieve this from the DB - users = ['pingou'] - - limit = APP.config['ITEM_PER_PAGE'] - start = limit * (page - 1) - end = limit * page - users_length = len(users) - users = users[start:end] - - total_page = int(ceil(users_length / float(limit))) - - return flask.render_template( - 'user_list.html', - users=users, - total_page=total_page, - page=page, - ) - - -@APP.route('/user/') -def view_user(username): - """ Front page of a specific user. - """ - - repopage = flask.request.args.get('repopage', 1) - try: - repopage = int(repopage) - except ValueError: - repopage = 1 - - forkpage = flask.request.args.get('forkpage', 1) - try: - forkpage = int(forkpage) - except ValueError: - forkpage = 1 - - limit = APP.config['ITEM_PER_PAGE'] - repo_start = limit * (repopage - 1) - fork_start = limit * (forkpage - 1) - - repos = progit.lib.list_projects( - SESSION, - username=flask.g.fas_user.username, - fork=False, - start=repo_start, - limit=limit) - repos_length = progit.lib.list_projects( - SESSION, - username=flask.g.fas_user.username, - fork=False, - count=True) - - forks = progit.lib.list_projects( - SESSION, - username=flask.g.fas_user.username, - fork=True, - start=fork_start, - limit=limit) - forks_length = progit.lib.list_projects( - SESSION, - username=flask.g.fas_user.username, - fork=True, - count=True) - - total_page_repos = int(ceil(repos_length / float(limit))) - total_page_forks = int(ceil(forks_length / float(limit))) - - repos_obj = [ - pygit2.Repository( - os.path.join(APP.config['GIT_FOLDER'], repo.path)) - for repo in repos] - - forks_obj = [ - pygit2.Repository( - os.path.join(APP.config['FORK_FOLDER'], repo.path)) - for repo in forks] - - return flask.render_template( - 'user_info.html', - username=username, - repos=repos, - repos_obj=repos_obj, - total_page_repos=total_page_repos, - forks=forks, - forks_obj=forks_obj, - total_page_forks=total_page_forks, - repopage=repopage, - forkpage=forkpage, - ) - - -@APP.route('/new/', methods=('GET', 'POST')) -def new_project(): - """ Form to create a new project. - """ - form = progit.forms.ProjectForm() - if form.validate_on_submit(): - name = form.name.data - description = form.description.data - - try: - message = progit.lib.new_project( - SESSION, - name=name, - description=description, - user=flask.g.fas_user.username, - folder=APP.config['GIT_FOLDER'], - ) - SESSION.commit() - flask.flash(message) - return flask.redirect(flask.url_for('view_repo', repo=name)) - except progit.exceptions.ProgitException, err: - flask.flash(str(err), 'error') - except SQLAlchemyError, err: # pragma: no cover - SESSION.rollback() - flask.flash(str(err), 'error') - - return flask.render_template( - 'new_project.html', - form=form, - ) - - -@APP.route('/') -def view_repo(repo): +def view_repo(repo, username=None): """ Front page of a specific repo. """ - repo = progit.lib.get_project(SESSION, repo) + repo = progit.lib.get_project(SESSION, repo, user=username) if repo is None: flask.abort(404) - repo_obj = pygit2.Repository(os.path.join(APP.config["GIT_FOLDER"], - repo.path)) + reponame = os.path.join(APP.config['GIT_FOLDER'], repo.path) + if repo.is_fork: + reponame = os.path.join(APP.config['FORK_FOLDER'], repo.path) + repo_obj = pygit2.Repository(reponame) cnt = 0 last_commits = [] @@ -217,30 +59,51 @@ def view_repo(repo): content = repo_obj[i.oid].data readme = progit.doc_utils.convert_readme(content, ext) + diff_commits = [] + if repo.is_fork: + parentname = os.path.join( + APP.config['GIT_FOLDER'], repo.parent.path) + orig_repo = pygit2.Repository(parentname) + + if not repo_obj.is_empty and not orig_repo.is_empty: + orig_commit = orig_repo[orig_repo.head.target] + repo_commit = repo_obj[repo_obj.head.target] + diff = repo_obj.diff( + repo_obj.revparse_single(orig_commit.oid.hex), + repo_obj.revparse_single(repo_commit.oid.hex)) + for commit in repo_obj.walk( + repo_obj.head.target, pygit2.GIT_SORT_TIME): + if commit.oid.hex == orig_commit.oid.hex: + break + diff_commits.append(commit.oid.hex) + return flask.render_template( 'repo_info.html', select='overview', repo=repo, repo_obj=repo_obj, + username=username, readme=readme, branches=sorted(repo_obj.listall_branches()), branchname='master', last_commits=last_commits, tree=tree, + diff_commits=diff_commits, ) -@APP.route('//branch/') -def view_repo_branch(repo, branchname): +def view_repo_branch(repo, branchname, username=None): """ Displays the information about a specific branch. """ - repo = progit.lib.get_project(SESSION, repo) + repo = progit.lib.get_project(SESSION, repo, user=username) - if repo is None: + if not repo: flask.abort(404) - repo_obj = pygit2.Repository(os.path.join(APP.config["GIT_FOLDER"], - repo.path)) + reponame = os.path.join(APP.config['GIT_FOLDER'], repo.path) + if repo.is_fork: + reponame = os.path.join(APP.config['FORK_FOLDER'], repo.path) + repo_obj = pygit2.Repository(reponame) if not branchname in repo_obj.listall_branches(): flask.abort(404) @@ -255,35 +118,57 @@ def view_repo_branch(repo, branchname): if cnt == 10: break + diff_commits = [] + if repo.is_fork: + parentname = os.path.join( + APP.config['GIT_FOLDER'], repo.parent.path) + orig_repo = pygit2.Repository(parentname) + + if not repo_obj.is_empty and not orig_repo.is_empty: + orig_commit = orig_repo[orig_repo.head.target] + repo_commit = repo_obj[branch.get_object().hex] + diff = repo_obj.diff( + repo_obj.revparse_single(orig_commit.oid.hex), + repo_obj.revparse_single(repo_commit.oid.hex)) + for commit in repo_obj.walk( + repo_obj.head.target, pygit2.GIT_SORT_TIME): + if commit.oid.hex == orig_commit.oid.hex: + break + diff_commits.append(commit.oid.hex) + return flask.render_template( 'repo_info.html', select='overview', repo=repo, + username=username, branches=sorted(repo_obj.listall_branches()), branchname=branchname, last_commits=last_commits, tree=sorted(last_commits[0].tree, key=lambda x: x.filemode), + diff_commits=diff_commits, ) -@APP.route('//log') -@APP.route('//log/') -def view_log(repo, branchname='master'): +def view_log(repo, branchname=None, username=None): """ Displays the logs of the specified repo. """ - repo = progit.lib.get_project(SESSION, repo) + repo = progit.lib.get_project(SESSION, repo, user=username) - if repo is None: + if not repo: flask.abort(404) - repo_obj = pygit2.Repository(os.path.join(APP.config["GIT_FOLDER"], - repo.path)) + reponame = os.path.join(APP.config['GIT_FOLDER'], repo.path) + if repo.is_fork: + reponame = os.path.join(APP.config['FORK_FOLDER'], repo.path) + repo_obj = pygit2.Repository(reponame) if branchname and not branchname in repo_obj.listall_branches(): flask.abort(404) - - branch = repo_obj.lookup_branch(branchname) + if branchname: + branch = repo_obj.lookup_branch(branchname) + else: + branch = repo_obj.lookup_branch('master') try: page = int(flask.request.args.get('page', 1)) @@ -304,31 +189,50 @@ def view_log(repo, branchname='master'): total_page = int(ceil(n_commits / float(limit))) + diff_commits = [] + if repo.is_fork: + parentname = os.path.join( + APP.config['GIT_FOLDER'], repo.parent.path) + orig_repo = pygit2.Repository(parentname) + if not repo_obj.is_empty and not orig_repo.is_empty: + orig_commit = orig_repo[orig_repo.head.target] + repo_commit = repo_obj[branch.get_object().hex] + diff = repo_obj.diff( + repo_obj.revparse_single(orig_commit.oid.hex), + repo_obj.revparse_single(repo_commit.oid.hex)) + for commit in repo_obj.walk( + repo_obj.head.target, pygit2.GIT_SORT_TIME): + if commit.oid.hex == orig_commit.oid.hex: + break + diff_commits.append(commit.oid.hex) + return flask.render_template( 'repo_info.html', select='logs', - origin='view_log', + origin='view_fork_log', repo=repo, + username=username, branches=sorted(repo_obj.listall_branches()), branchname=branchname, last_commits=last_commits, + diff_commits=diff_commits, page=page, total_page=total_page, ) -@APP.route('//blob//') -@APP.route('//blob//') -def view_file(repo, identifier, filename): +def view_file(repo, identifier, filename, username=None): """ Displays the content of a file or a tree for the specified repo. """ - repo = progit.lib.get_project(SESSION, repo) + repo = progit.lib.get_project(SESSION, repo, user=username) - if repo is None: + if not repo: flask.abort(404) - repo_obj = pygit2.Repository(os.path.join(APP.config["GIT_FOLDER"], - repo.path)) + reponame = os.path.join(APP.config['GIT_FOLDER'], repo.path) + if repo.is_fork: + reponame = os.path.join(APP.config['FORK_FOLDER'], repo.path) + repo_obj = pygit2.Repository(reponame) if identifier in repo_obj.listall_branches(): branchname = identifier @@ -365,6 +269,7 @@ def view_file(repo, identifier, filename): 'file.html', select='tree', repo=repo, + username=username, branchname=branchname, filename=filename, content=content, @@ -372,17 +277,18 @@ def view_file(repo, identifier, filename): ) -@APP.route('//') -def view_commit(repo, commitid): +def view_commit(repo, commitid, username=None): """ Render a commit in a repo """ - repo = progit.lib.get_project(SESSION, repo) + repo = progit.lib.get_project(SESSION, repo, user=username) - if repo is None: + if not repo: flask.abort(404) - repo_obj = pygit2.Repository(os.path.join(APP.config["GIT_FOLDER"], - repo.path)) + reponame = os.path.join(APP.config['GIT_FOLDER'], repo.path) + if repo.is_fork: + reponame = os.path.join(APP.config['FORK_FOLDER'], repo.path) + repo_obj = pygit2.Repository(reponame) try: commit = repo_obj.get(commitid) @@ -410,6 +316,7 @@ def view_commit(repo, commitid): 'commit.html', select='logs', repo=repo, + username=username, commitid=commitid, commit=commit, diff=diff, @@ -417,9 +324,7 @@ def view_commit(repo, commitid): ) -@APP.route('//tree/') -@APP.route('//tree/') -def view_tree(repo, identifier=None): +def view_tree(repo, identifier=None, username=None): """ Render the tree of the repo """ repo = progit.lib.get_project(SESSION, repo) @@ -427,8 +332,10 @@ def view_tree(repo, identifier=None): if repo is None: flask.abort(404) - repo_obj = pygit2.Repository(os.path.join(APP.config["GIT_FOLDER"], - repo.path)) + reponame = os.path.join(APP.config['GIT_FOLDER'], repo.path) + if repo.is_fork: + reponame = os.path.join(APP.config['FORK_FOLDER'], repo.path) + repo_obj = pygit2.Repository(reponame) if identifier in repo_obj.listall_branches(): branchname = identifier @@ -450,6 +357,7 @@ def view_tree(repo, identifier=None): 'file.html', select='tree', repo=repo, + username=username, branchname=branchname, filename='', content=content, @@ -457,11 +365,10 @@ def view_tree(repo, identifier=None): ) -@APP.route('//issues') -def view_issues(repo): +def view_issues(repo, username=None): """ List all issues associated to a repo """ - repo = progit.lib.get_project(SESSION, repo) + repo = progit.lib.get_project(SESSION, repo, user=username) if repo is None: flask.abort(404) @@ -472,15 +379,15 @@ def view_issues(repo): 'issues.html', select='issues', repo=repo, + username=username, issues=issues, ) -@APP.route('//new_issue', methods=('GET', 'POST')) -def new_issue(repo): +def new_issue(repo, username=None): """ Create a new issue """ - repo = progit.lib.get_project(SESSION, repo) + repo = progit.lib.get_project(SESSION, repo, user=username) if repo is None: flask.abort(404, 'Project not found') @@ -501,7 +408,7 @@ def new_issue(repo): SESSION.commit() flask.flash(message) return flask.redirect(flask.url_for( - 'view_issues', repo=repo.name)) + 'view_fork_issues', username=username, repo=repo.name)) except progit.exceptions.ProgitException, err: flask.flash(str(err), 'error') except SQLAlchemyError, err: # pragma: no cover @@ -513,14 +420,14 @@ def new_issue(repo): select='issues', form=form, repo=repo, + username=username, ) -@APP.route('//issue/') -def view_issue(repo, issueid): +def view_issue(repo, issueid, username=None): """ List all issues associated to a repo """ - repo = progit.lib.get_project(SESSION, repo) + repo = progit.lib.get_project(SESSION, repo, user=username) if repo is None: flask.abort(404, 'Project not found') @@ -534,15 +441,15 @@ def view_issue(repo, issueid): 'issue.html', select='issues', repo=repo, + username=username, issue=issue, ) -@APP.route('//issue//edit', methods=('GET', 'POST')) -def edit_issue(repo, issueid): +def edit_issue(repo, issueid, username=None): """ Edit the specified issue """ - repo = progit.lib.get_project(SESSION, repo) + repo = progit.lib.get_project(SESSION, repo, user=username) if repo is None: flask.abort(404, 'Project not found') @@ -567,7 +474,7 @@ def edit_issue(repo, issueid): SESSION.commit() flask.flash(message) return flask.redirect(flask.url_for( - 'view_issues', repo=repo.name)) + 'view_fork_issues', username=username, repo=repo.name)) except progit.exceptions.ProgitException, err: flask.flash(str(err), 'error') except SQLAlchemyError, err: # pragma: no cover @@ -583,5 +490,80 @@ def edit_issue(repo, issueid): type='edit', form=form, repo=repo, + username=username, issue=issue, ) + + +def request_pull(repo, requestid=None, username=None): + """ Request pulling the changes from the fork into the project. + """ + repo = progit.lib.get_project(SESSION, repo, user=username) + + if not repo: + flask.abort(404, 'Project not found') + + request = progit.lib.get_pull_request( + SESSION, project_id=repo.id, requestid=requestid) + + if not request: + flask.abort(404, 'Pull-request not found') + + repopath = os.path.join( + APP.config['FORK_FOLDER'], request.repo_from.path) + repo_obj = pygit2.Repository(repopath) + + parentname = os.path.join( + APP.config['GIT_FOLDER'], request.repo.path) + orig_repo = pygit2.Repository(parentname) + + diff_commits = [] + diffs = [] + repo_commit = repo_obj[request.stop_id] + if not repo_obj.is_empty and not orig_repo.is_empty: + orig_commit = orig_repo[request.start_id] + + for commit in repo_obj.walk(request.stop_id, pygit2.GIT_SORT_TIME): + if commit.oid.hex == orig_commit.oid.hex: + break + diff_commits.append(commit) + diffs.append( + repo_obj.diff( + repo_obj.revparse_single(commit.parents[0].oid.hex), + repo_obj.revparse_single(commit.oid.hex) + ) + ) + + elif orig_repo.is_empty: + orig_commit = None + diff = repo_commit.tree.diff_to_tree(swap=True) + else: + flask.flash( + 'Fork is empty, there are no commits to request pulling', + 'error') + return flask.redirect(flask.url_for( + 'view_fork_repo', username=username, repo=repo.name)) + + html_diffs = [] + for diff in diffs: + html_diffs.append( + highlight( + diff.patch, + DiffLexer(), + HtmlFormatter( + noclasses=True, + style="tango",) + ) + ) + + return flask.render_template( + 'pull_request.html', + repo=repo, + username=username or request.user, + request=request, + repo_obj=repo_obj, + orig_repo=orig_repo, + diff_commits=diff_commits, + diffs=diffs, + html_diffs=html_diffs, + ) diff --git a/progit/fork.py b/progit/fork.py index da838a8..02b375d 100644 --- a/progit/fork.py +++ b/progit/fork.py @@ -10,6 +10,7 @@ import flask import os +import tempfile from math import ceil import pygit2 @@ -22,6 +23,7 @@ from pygments.formatters import HtmlFormatter import progit.doc_utils import progit.lib +import progit.forms from progit import APP, SESSION, LOG, __get_file_in_tree @@ -60,474 +62,11 @@ def fork_project(repo, username=None): return flask.redirect(flask.url_for('view_repo',repo=repo.name)) -@APP.route('/fork//') -def view_fork_repo(username, repo): - """ Front page of a specific repo. - """ - repo = progit.lib.get_project(SESSION, repo, user=username) - - if not repo: - flask.abort(404) - - reponame = os.path.join(APP.config['FORK_FOLDER'], repo.path) - repo_obj = pygit2.Repository(reponame) - - cnt = 0 - last_commits = [] - tree = [] - if not repo_obj.is_empty: - for commit in repo_obj.walk( - repo_obj.head.target, pygit2.GIT_SORT_TIME): - last_commits.append(commit) - cnt += 1 - if cnt == 10: - break - tree = sorted(last_commits[0].tree, key=lambda x: x.filemode) - - readme = None - for i in tree: - name, ext = os.path.splitext(i.name) - if name == 'README': - content = repo_obj[i.oid].data - readme = progit.doc_utils.convert_readme(content, ext) - - parentname = os.path.join(APP.config['GIT_FOLDER'], repo.parent.path) - orig_repo = pygit2.Repository(parentname) - - diff_commits = [] - if not repo_obj.is_empty and not orig_repo.is_empty: - orig_commit = orig_repo[orig_repo.head.target] - repo_commit = repo_obj[repo_obj.head.target] - diff = repo_obj.diff( - repo_obj.revparse_single(orig_commit.oid.hex), - repo_obj.revparse_single(repo_commit.oid.hex)) - for commit in repo_obj.walk( - repo_obj.head.target, pygit2.GIT_SORT_TIME): - if commit.oid.hex == orig_commit.oid.hex: - break - diff_commits.append(commit.oid.hex) - - return flask.render_template( - 'repo_info.html', - select='overview', - repo=repo, - repo_obj=repo_obj, - username=username, - readme=readme, - branches=sorted(repo_obj.listall_branches()), - branchname='master', - last_commits=last_commits, - tree=tree, - diff_commits=diff_commits, - ) - - -@APP.route('/fork///branch/') -def view_fork_repo_branch(username, repo, branchname): - """ Displays the information about a specific branch. - """ - repo = progit.lib.get_project(SESSION, repo, user=username) - - if not repo: - flask.abort(404) - - reponame = os.path.join(APP.config['FORK_FOLDER'], repo.path) - repo_obj = pygit2.Repository(reponame) - - if not branchname in repo_obj.listall_branches(): - flask.abort(404) - - branch = repo_obj.lookup_branch(branchname) - - cnt = 0 - last_commits = [] - for commit in repo_obj.walk(branch.get_object().hex, pygit2.GIT_SORT_TIME): - last_commits.append(commit) - cnt += 1 - if cnt == 10: - break - - parentname = os.path.join(APP.config['GIT_FOLDER'], repo.parent.path) - orig_repo = pygit2.Repository(parentname) - - diff_commits = [] - if not repo_obj.is_empty and not orig_repo.is_empty: - orig_commit = orig_repo[orig_repo.head.target] - repo_commit = repo_obj[branch.get_object().hex] - diff = repo_obj.diff( - repo_obj.revparse_single(orig_commit.oid.hex), - repo_obj.revparse_single(repo_commit.oid.hex)) - for commit in repo_obj.walk( - repo_obj.head.target, pygit2.GIT_SORT_TIME): - if commit.oid.hex == orig_commit.oid.hex: - break - diff_commits.append(commit.oid.hex) - - return flask.render_template( - 'repo_info.html', - select='overview', - repo=repo, - username=username, - branches=sorted(repo_obj.listall_branches()), - branchname=branchname, - last_commits=last_commits, - tree=sorted(last_commits[0].tree, key=lambda x: x.filemode), - diff_commits=diff_commits, - ) - - -@APP.route('/fork///log') -@APP.route('/fork///log/') -def view_fork_log(username, repo, branchname=None): - """ Displays the logs of the specified repo. - """ - repo = progit.lib.get_project(SESSION, repo, user=username) - - if not repo: - flask.abort(404) - - reponame = os.path.join(APP.config['FORK_FOLDER'], repo.path) - repo_obj = pygit2.Repository(reponame) - - if branchname and not branchname in repo_obj.listall_branches(): - flask.abort(404) - - if branchname: - branch = repo_obj.lookup_branch(branchname) - else: - branch = repo_obj.lookup_branch('master') - - try: - page = int(flask.request.args.get('page', 1)) - except ValueError: - page = 1 - - limit = APP.config['ITEM_PER_PAGE'] - start = limit * (page - 1) - end = limit * page - - n_commits = 0 - last_commits = [] - for commit in repo_obj.walk( - branch.get_object().hex, pygit2.GIT_SORT_TIME): - if n_commits >= start and n_commits <= end: - last_commits.append(commit) - n_commits += 1 - - total_page = int(ceil(n_commits / float(limit))) - - parentname = os.path.join(APP.config['GIT_FOLDER'], repo.parent.path) - orig_repo = pygit2.Repository(parentname) - - diff_commits = [] - if not repo_obj.is_empty and not orig_repo.is_empty: - orig_commit = orig_repo[orig_repo.head.target] - repo_commit = repo_obj[branch.get_object().hex] - diff = repo_obj.diff( - repo_obj.revparse_single(orig_commit.oid.hex), - repo_obj.revparse_single(repo_commit.oid.hex)) - for commit in repo_obj.walk( - repo_obj.head.target, pygit2.GIT_SORT_TIME): - if commit.oid.hex == orig_commit.oid.hex: - break - diff_commits.append(commit.oid.hex) - - return flask.render_template( - 'repo_info.html', - select='logs', - origin='view_fork_log', - repo=repo, - username=username, - branches=sorted(repo_obj.listall_branches()), - branchname=branchname, - last_commits=last_commits, - diff_commits=diff_commits, - page=page, - total_page=total_page, - ) - - -@APP.route('/fork///blob//') -@APP.route('/fork///blob//') -def view_fork_file(username, repo, identifier, filename): - """ Displays the content of a file or a tree for the specified repo. - """ - repo = progit.lib.get_project(SESSION, repo, user=username) - - if not repo: - flask.abort(404) - - reponame = os.path.join(APP.config['FORK_FOLDER'], repo.path) - repo_obj = pygit2.Repository(reponame) - - if identifier in repo_obj.listall_branches(): - branchname = identifier - branch = repo_obj.lookup_branch(identifier) - commit = branch.get_object() - else: - try: - commit = repo_obj.get(identifier) - branchname = identifier - except ValueError: - # If it's not a commit id then it's part of the filename - commit = repo_obj[repo_obj.head.target] - branchname = 'master' - - content = __get_file_in_tree(repo_obj, commit.tree, filename.split('/')) - if not content: - flask.abort(404, 'File not found') - - content = repo_obj[content.oid] - if isinstance(content, pygit2.Blob): - content = highlight( - content.data, - guess_lexer(content.data), - HtmlFormatter( - noclasses=True, - style="tango",) - ) - output_type = 'file' - else: - content = sorted(content, key=lambda x: x.filemode) - output_type = 'tree' - - return flask.render_template( - 'file.html', - select='tree', - repo=repo, - username=username, - branchname=branchname, - filename=filename, - content=content, - output_type=output_type, - ) - - -@APP.route('/fork///') -def view_fork_commit(username, repo, commitid): - """ Render a commit in a repo - """ - repo = progit.lib.get_project(SESSION, repo, user=username) - - if not repo: - flask.abort(404) - - reponame = os.path.join(APP.config['FORK_FOLDER'], repo.path) - repo_obj = pygit2.Repository(reponame) - - try: - commit = repo_obj.get(commitid) - except ValueError: - flask.abort(404) - - if commit.parents: - diff = commit.tree.diff_to_tree() - - parent = repo_obj.revparse_single('%s^' % commitid) - diff = repo_obj.diff(parent, commit) - else: - # First commit in the repo - diff = commit.tree.diff_to_tree(swap=True) - - html_diff = highlight( - diff.patch, - DiffLexer(), - HtmlFormatter( - noclasses=True, - style="tango",) - ) - - return flask.render_template( - 'commit.html', - select='logs', - repo=repo, - username=username, - commitid=commitid, - commit=commit, - diff=diff, - html_diff=html_diff, - ) - - -@APP.route('/fork///tree/') -@APP.route('/fork///tree/') -def view_fork_tree(username, repo, identifier=None): - """ Render the tree of the repo - """ - repo = progit.lib.get_project(SESSION, repo, user=username) - - if not repo: - flask.abort(404) - - reponame = os.path.join(APP.config['FORK_FOLDER'], repo.path) - repo_obj = pygit2.Repository(reponame) - - if identifier in repo_obj.listall_branches(): - branchname = identifier - branch = repo_obj.lookup_branch(identifier) - commit = branch.get_object() - else: - try: - commit = repo_obj.get(identifier) - branchname = identifier - except (ValueError, TypeError): - # If it's not a commit id then it's part of the filename - commit = repo_obj[repo_obj.head.target] - branchname = 'master' - - content = sorted(commit.tree, key=lambda x: x.filemode) - output_type = 'tree' - - return flask.render_template( - 'file.html', - select='tree', - repo=repo, - username=username, - branchname=branchname, - filename='', - content=content, - output_type=output_type, - ) - - -@APP.route('/fork///issues') -def view_fork_issues(repo, username): - """ List all issues associated to a repo - """ - repo = progit.lib.get_project(SESSION, repo, user=username) - - if repo is None: - flask.abort(404) - - issues = progit.lib.get_issues(SESSION, repo) - - return flask.render_template( - 'issues.html', - select='issues', - repo=repo, - username=username, - issues=issues, - ) - - -@APP.route('/fork///new_issue', methods=('GET', 'POST')) -def fork_new_issue(username, repo): - """ Create a new issue - """ - repo = progit.lib.get_project(SESSION, repo, user=username) - - if repo is None: - flask.abort(404, 'Project not found') - - form = progit.forms.IssueForm() - if form.validate_on_submit(): - title = form.title.data - content = form.content.data - - try: - message = progit.lib.new_issue( - SESSION, - repo=repo, - title=title, - content=content, - user=flask.g.fas_user.username, - ) - SESSION.commit() - flask.flash(message) - return flask.redirect(flask.url_for( - 'view_fork_issues', username=username, repo=repo.name)) - except progit.exceptions.ProgitException, err: - flask.flash(str(err), 'error') - except SQLAlchemyError, err: # pragma: no cover - SESSION.rollback() - flask.flash(str(err), 'error') - - return flask.render_template( - 'new_issue.html', - select='issues', - form=form, - repo=repo, - username=username, - ) - - -@APP.route('/fork///issue/') -def view_fork_issue(username, repo, issueid): - """ List all issues associated to a repo - """ - repo = progit.lib.get_project(SESSION, repo, user=username) - - if repo is None: - flask.abort(404, 'Project not found') - - issue = progit.lib.get_issue(SESSION, issueid) - - if issue is None: - flask.abort(404, 'Issue not found') - - return flask.render_template( - 'issue.html', - select='issues', - repo=repo, - issue=issue, - username=username, - ) - - -@APP.route('/fork///issue//edit', +@APP.route('/fork///request-pull/new', methods=('GET', 'POST')) -def fork_edit_issue(username, repo, issueid): - """ Edit the specified issue opened against a fork - """ - repo = progit.lib.get_project(SESSION, repo, user=username) - - if repo is None: - flask.abort(404, 'Project not found') - - issue = progit.lib.get_issue(SESSION, issueid) - - if issue is None: - flask.abort(404, 'Issue not found') - - form = progit.forms.IssueForm() - if form.validate_on_submit(): - title = form.title.data - content = form.content.data - - try: - message = progit.lib.edit_issue( - SESSION, - issue=issue, - title=title, - content=content, - ) - SESSION.commit() - flask.flash(message) - return flask.redirect(flask.url_for( - 'view_fork_issues', username=username, repo=repo.name)) - except progit.exceptions.ProgitException, err: - flask.flash(str(err), 'error') - except SQLAlchemyError, err: # pragma: no cover - SESSION.rollback() - flask.flash(str(err), 'error') - elif flask.request.method == 'GET': - form.title.data = issue.title - form.content.data = issue.content - - return flask.render_template( - 'new_issue.html', - select='issues', - type='edit', - form=form, - repo=repo, - username=username, - issue=issue, - ) - - -@APP.route('/fork///request-pull') -@APP.route('/fork///request-pull/') -def request_pull(username, repo, commitid=None): +@APP.route('/fork///request-pull/new/', + methods=('GET', 'POST')) +def new_request_pull(username, repo, commitid=None): """ Request pulling the changes from the fork into the project. """ repo = progit.lib.get_project(SESSION, repo, user=username) @@ -562,6 +101,7 @@ def request_pull(username, repo, commitid=None): ) elif orig_repo.is_empty: + orig_commit = None repo_commit = repo_obj[repo_obj.head.target] diff = repo_commit.tree.diff_to_tree(swap=True) else: @@ -583,13 +123,39 @@ def request_pull(username, repo, commitid=None): ) ) + form = progit.forms.RequestPullForm() + if form.validate_on_submit(): + try: + if orig_commit: + orig_commit = orig_commit.oid.hex + message = progit.lib.new_pull_request( + SESSION, + repo=repo.parent, + repo_from=repo, + title=form.title.data, + start_id=orig_commit, + stop_id=repo_commit.oid.hex, + user=flask.g.fas_user.username, + ) + SESSION.commit() + flask.flash(message) + return flask.redirect(flask.url_for( + 'view_fork_issues', username=username, repo=repo.name)) + except progit.exceptions.ProgitException, err: + flask.flash(str(err), 'error') + except SQLAlchemyError, err: # pragma: no cover + SESSION.rollback() + flask.flash(str(err), 'error') + return flask.render_template( 'pull_request.html', repo=repo, username=username, + commitid=commitid, repo_obj=repo_obj, orig_repo=orig_repo, diff_commits=diff_commits, diffs=diffs, html_diffs=html_diffs, + form=form, ) diff --git a/progit/urls.py b/progit/urls.py new file mode 100644 index 0000000..6c95c9b --- /dev/null +++ b/progit/urls.py @@ -0,0 +1,346 @@ +#-*- coding: utf-8 -*- + +""" + (c) 2014 - Copyright Red Hat Inc + + Authors: + Pierre-Yves Chibon + +""" + +import flask +import os +from math import ceil + +import pygit2 +from sqlalchemy.exc import SQLAlchemyError +from pygments import highlight +from pygments.lexers import guess_lexer +from pygments.lexers.text import DiffLexer +from pygments.formatters import HtmlFormatter + + +import progit.app +import progit.exceptions +import progit.lib +import progit.forms +from progit import APP, SESSION, LOG, __get_file_in_tree + + +### Application +@APP.route('/') +def index(): + """ Front page of the application. + """ + page = flask.request.args.get('page', 1) + try: + page = int(page) + except ValueError: + page = 1 + + limit = APP.config['ITEM_PER_PAGE'] + start = limit * (page - 1) + + repos = progit.lib.list_projects(SESSION, fork=False, start=start, limit=limit) + num_repos = progit.lib.list_projects(SESSION, fork=False, count=True) + + total_page = int(ceil(num_repos / float(limit))) + + return flask.render_template( + 'index.html', + repos=repos, + total_page=total_page, + page=page, + ) + + +@APP.route('/users/') +def view_users(): + """ Present the list of users. + """ + page = flask.request.args.get('page', 1) + try: + page = int(page) + except ValueError: + page = 1 + + ## TODO: retrieve this from the DB + users = ['pingou'] + + limit = APP.config['ITEM_PER_PAGE'] + start = limit * (page - 1) + end = limit * page + users_length = len(users) + users = users[start:end] + + total_page = int(ceil(users_length / float(limit))) + + return flask.render_template( + 'user_list.html', + users=users, + total_page=total_page, + page=page, + ) + + +@APP.route('/user/') +def view_user(username): + """ Front page of a specific user. + """ + + repopage = flask.request.args.get('repopage', 1) + try: + repopage = int(repopage) + except ValueError: + repopage = 1 + + forkpage = flask.request.args.get('forkpage', 1) + try: + forkpage = int(forkpage) + except ValueError: + forkpage = 1 + + limit = APP.config['ITEM_PER_PAGE'] + repo_start = limit * (repopage - 1) + fork_start = limit * (forkpage - 1) + + repos = progit.lib.list_projects( + SESSION, + username=flask.g.fas_user.username, + fork=False, + start=repo_start, + limit=limit) + repos_length = progit.lib.list_projects( + SESSION, + username=flask.g.fas_user.username, + fork=False, + count=True) + + forks = progit.lib.list_projects( + SESSION, + username=flask.g.fas_user.username, + fork=True, + start=fork_start, + limit=limit) + forks_length = progit.lib.list_projects( + SESSION, + username=flask.g.fas_user.username, + fork=True, + count=True) + + total_page_repos = int(ceil(repos_length / float(limit))) + total_page_forks = int(ceil(forks_length / float(limit))) + + repos_obj = [ + pygit2.Repository( + os.path.join(APP.config['GIT_FOLDER'], repo.path)) + for repo in repos] + + forks_obj = [ + pygit2.Repository( + os.path.join(APP.config['FORK_FOLDER'], repo.path)) + for repo in forks] + + return flask.render_template( + 'user_info.html', + username=username, + repos=repos, + repos_obj=repos_obj, + total_page_repos=total_page_repos, + forks=forks, + forks_obj=forks_obj, + total_page_forks=total_page_forks, + repopage=repopage, + forkpage=forkpage, + ) + + +@APP.route('/new/', methods=('GET', 'POST')) +def new_project(): + """ Form to create a new project. + """ + form = progit.forms.ProjectForm() + if form.validate_on_submit(): + name = form.name.data + description = form.description.data + + try: + message = progit.lib.new_project( + SESSION, + name=name, + description=description, + user=flask.g.fas_user.username, + folder=APP.config['GIT_FOLDER'], + ) + SESSION.commit() + flask.flash(message) + return flask.redirect(flask.url_for('view_repo', repo=name)) + except progit.exceptions.ProgitException, err: + flask.flash(str(err), 'error') + except SQLAlchemyError, err: # pragma: no cover + SESSION.rollback() + flask.flash(str(err), 'error') + + return flask.render_template( + 'new_project.html', + form=form, + ) + + +@APP.route('/') +def view_repo(repo): + """ Front page of a specific repo. + """ + return progit.app.view_repo(repo=repo) + + +@APP.route('/fork//') +def view_fork_repo(username, repo): + """ Front page of a specific repo. + """ + return progit.app.view_repo(repo=repo, username=username) + + +@APP.route('//branch/') +def view_repo_branch(repo, branchname): + return progit.app.view_repo_branch(repo, branchname) + + +@APP.route('/fork///branch/') +def view_fork_repo_branch(username, repo, branchname): + """ Displays the information about a specific branch. + """ + return progit.app.view_repo_branch(repo, branchname, username=username) + + +@APP.route('//log') +@APP.route('//log/') +def view_log(repo, branchname=None): + """ Displays the logs of the specified repo. + """ + return progit.app.view_log(repo, branchname) + + +@APP.route('/fork///log') +@APP.route('/fork///log/') +def view_fork_log(username, repo, branchname=None): + """ Displays the logs of the specified repo. + """ + return progit.app.view_log(repo, branchname, username=username) + + +@APP.route('//blob//') +@APP.route('//blob//') +def view_file(repo, identifier, filename): + """ Displays the content of a file or a tree for the specified repo. + """ + return progit.app.view_file(repo, identifier, filename) + + +@APP.route('/fork///blob//') +@APP.route('/fork///blob//') +def view_fork_file(username, repo, identifier, filename): + """ Displays the content of a file or a tree for the specified repo. + """ + return progit.app.view_file(repo, identifier, filename, username=username) + + +@APP.route('//') +def view_commit(repo, commitid): + """ Render a commit in a repo + """ + return progit.app.view_commit(repo, commitid) + + +@APP.route('/fork///') +def view_fork_commit(username, repo, commitid): + """ Render a commit in a repo + """ + return progit.app.view_commit(repo, commitid, username=username) + + +@APP.route('//tree/') +@APP.route('//tree/') +def view_tree(repo, identifier=None): + """ Render the tree of the repo + """ + return progit.app.view_tree(repo, identifier=identifier) + + +@APP.route('/fork///tree/') +@APP.route('/fork///tree/') +def view_fork_tree(username, repo, identifier=None): + """ Render the tree of the repo + """ + return progit.app.view_tree(repo, identifier=identifier, username=username) + + +@APP.route('//issues') +def view_issues(repo): + """ List all issues associated to a repo + """ + return progit.app.view_issues(repo) + + +@APP.route('/fork///issues') +def view_fork_issues(repo, username): + """ List all issues associated to a repo + """ + return progit.app.view_issues(repo, username=username) + + +@APP.route('//new_issue', methods=('GET', 'POST')) +def new_issue(repo): + """ Create a new issue + """ + return progit.app.new_issue(repo) + + +@APP.route('/fork///new_issue', methods=('GET', 'POST')) +def fork_new_issue(username, repo): + """ Create a new issue + """ + return progit.app.new_issue(repo, username=username) + + +@APP.route('//issue/') +def view_issue(repo, issueid): + """ List all issues associated to a repo + """ + return progit.app.view_issue(repo, issueid) + + +@APP.route('/fork///issue/') +def view_fork_issue(username, repo, issueid): + """ List all issues associated to a repo + """ + return progit.app.view_issue(repo, issueid, username=username) + + +@APP.route('//issue//edit', methods=('GET', 'POST')) +def edit_issue(repo, issueid): + """ Edit the specified issue + """ + return progit.app.edit_issue(repo, issueid) + + +@APP.route('/fork///issue//edit', + methods=('GET', 'POST')) +def fork_edit_issue(username, repo, issueid): + """ Edit the specified issue opened against a fork + """ + return progit.app.edit_issue(repo, issueid, username=username) + + +@APP.route('//request-pull/') +def request_pull(repo, requestid=None): + """ Request pulling the changes from the fork into the project. + """ + return progit.app.request_pull(repo, requestid=requestid) + + +@APP.route('/fork///request-pull/') +def fork_request_pull(username, repo, requestid=None): + """ Request pulling the changes from the fork into the project. + """ + return progit.app.request_pull(repo, requestid=requestid, username=username)