diff --git a/pagure/internal/__init__.py b/pagure/internal/__init__.py
index d1180fc..c39dbb0 100644
--- a/pagure/internal/__init__.py
+++ b/pagure/internal/__init__.py
@@ -10,6 +10,7 @@ Internal endpoints.
"""
+import collections
import os
import flask
@@ -493,3 +494,80 @@ def get_branches_of_commit():
'branches': branches,
}
)
+
+
+@PV.route('/branches/heads/', methods=['POST'])
+@localonly
+def get_branches_head():
+ """ Return the heads of each branch in the repo, using the following
+ structure:
+ {
+ code: 'OK',
+ branches: {
+ name : commit,
+ ...
+ },
+ heads: {
+ commit : [branch, ...],
+ ...
+ }
+ }
+ """
+ form = pagure.forms.ConfirmationForm()
+ if not form.validate_on_submit():
+ response = flask.jsonify({
+ 'code': 'ERROR',
+ 'message': 'Invalid input submitted',
+ })
+ response.status_code = 400
+ return response
+
+ repo = pagure.get_authorized_project(
+ pagure.SESSION,
+ flask.request.form.get('repo', '').strip() or None,
+ namespace=flask.request.form.get('namespace', '').strip() or None,
+ user=flask.request.form.get('repouser', '').strip() or None)
+
+ if not repo:
+ response = flask.jsonify({
+ 'code': 'ERROR',
+ 'message': 'No repo found with the information provided',
+ })
+ response.status_code = 404
+ return response
+
+ repopath = os.path.join(pagure.APP.config['GIT_FOLDER'], repo.path)
+
+ if not os.path.exists(repopath):
+ response = flask.jsonify({
+ 'code': 'ERROR',
+ 'message': 'No git repo found with the information provided',
+ })
+ response.status_code = 404
+ return response
+
+ repo_obj = pygit2.Repository(repopath)
+ if repo.is_fork:
+ parentreponame = pagure.get_repo_path(repo.parent)
+ parent_repo_obj = pygit2.Repository(parentreponame)
+ else:
+ parent_repo_obj = repo_obj
+
+ branches = {}
+ if not repo_obj.is_empty and repo_obj.listall_branches() > 1:
+ for branchname in repo_obj.listall_branches():
+ branch = repo_obj.lookup_branch(branchname)
+ branches[branchname] = branch.get_object().hex
+
+ # invert the dict
+ heads = collections.defaultdict(list)
+ for branch, commit in branches.items():
+ heads[commit].append(branch)
+
+ return flask.jsonify(
+ {
+ 'code': 'OK',
+ 'branches': branches,
+ 'heads': heads,
+ }
+ )
diff --git a/pagure/templates/commit.html b/pagure/templates/commit.html
index b52b680..0534892 100644
--- a/pagure/templates/commit.html
+++ b/pagure/templates/commit.html
@@ -197,7 +197,7 @@
data: {
repo: "{{ repo.name }}",
repouser: "{{ repo.user.user if repo.is_fork else '' }}",
- namespace: "{{ repo.namespace }}",
+ namespace: "{{ repo.namespace if repo.namespace else '' }}",
commit_id: "{{ commitid }}",
csrf_token: "{{ form.csrf_token.current_token }}",
},
diff --git a/pagure/templates/commits.html b/pagure/templates/commits.html
index 92a37dc..44779ef 100644
--- a/pagure/templates/commits.html
+++ b/pagure/templates/commits.html
@@ -156,8 +156,10 @@
namespace=repo.namespace,
author=commit.author.email),
cssclass="notblue")|safe}}
-
-
{{ commit.hex|short }}
+
+
+ {{ commit.hex|short }}
+
{% endfor %}
@@ -176,6 +178,32 @@
$('#diff_commits_link').click(function(){
$('#diff_commits').toggle();
});
+ $.ajax({
+ url: '{{ url_for("internal_ns.get_branches_head") }}' ,
+ type: 'POST',
+ data: {
+ repo: "{{ repo.name }}",
+ repouser: "{{ repo.user.user if repo.is_fork else '' }}",
+ namespace: "{{ repo.namespace if repo.namespace else '' }}",
+ csrf_token: "{{ form.csrf_token.current_token }}",
+ },
+ dataType: 'json',
+ success: function(res) {
+ for (var _c in res.heads) {
+ var _b = '';
+ for (var i=0; i < res.heads[_c].length; i++){
+ _b += res.heads[_c][i] + ', ';
+ }
+ _b = _b.slice(0, -2);
+ var html = ''
+ + ' '
+ + _b + '';
+ var el = $('#c_' + _c);
+ el.parent().before(html);
+ }
+ },
+ });
});
{% endblock %}
diff --git a/tests/test_pagure_flask_internal.py b/tests/test_pagure_flask_internal.py
index 9b4eba9..fffb04e 100644
--- a/tests/test_pagure_flask_internal.py
+++ b/tests/test_pagure_flask_internal.py
@@ -41,6 +41,7 @@ class PagureFlaskInternaltests(tests.Modeltests):
pagure.APP.config['IP_ALLOWED_INTERNAL'] + [None]))
pagure.SESSION = self.session
pagure.internal.SESSION = self.session
+ pagure.ui.app.SESSION = self.session
pagure.ui.repo.SESSION = self.session
pagure.ui.filters.SESSION = self.session
@@ -1181,6 +1182,177 @@ class PagureFlaskInternaltests(tests.Modeltests):
}
)
+ def test_get_branches_head(self):
+ ''' Test the get_branches_head from the internal API. '''
+ tests.create_projects(self.session)
+ tests.create_projects_git(os.path.join(self.path, 'repos'))
+
+ user = tests.FakeUser()
+ user.username = 'pingou'
+ with tests.user_set(pagure.APP, user):
+ csrf_token = self.get_csrf()
+
+ # No CSRF token
+ data = {
+ 'repo': 'fakerepo',
+ }
+ output = self.app.post('/pv/branches/heads/', data=data)
+ self.assertEqual(output.status_code, 400)
+ js_data = json.loads(output.data.decode('utf-8'))
+ self.assertDictEqual(
+ js_data,
+ {u'code': u'ERROR', u'message': u'Invalid input submitted'}
+ )
+
+ # Invalid repo
+ data = {
+ 'repo': 'fakerepo',
+ 'commit_id': 'foo',
+ 'csrf_token': csrf_token,
+ }
+ output = self.app.post('/pv/branches/heads/', data=data)
+ self.assertEqual(output.status_code, 404)
+ js_data = json.loads(output.data.decode('utf-8'))
+ self.assertDictEqual(
+ js_data,
+ {
+ u'code': u'ERROR',
+ u'message': u'No repo found with the information provided'
+ }
+ )
+
+ # Rigth repo, no commit
+ data = {
+ 'repo': 'test',
+ 'csrf_token': csrf_token,
+ }
+
+ output = self.app.post('/pv/branches/heads/', data=data)
+ self.assertEqual(output.status_code, 200)
+ js_data = json.loads(output.data.decode('utf-8'))
+ self.assertDictEqual(
+ js_data,
+ {u"branches": {}, u"code": u"OK", u"heads": {}}
+ )
+
+ # Request is fine, but git repo doesn't exist
+ item = pagure.lib.model.Project(
+ user_id=1, # pingou
+ name='test20',
+ description='test project #20',
+ hook_token='aaabbbhhh',
+ )
+ self.session.add(item)
+ self.session.commit()
+
+ data = {
+ 'repo': 'test20',
+ 'csrf_token': csrf_token,
+ }
+ output = self.app.post('/pv/branches/heads/', data=data)
+ self.assertEqual(output.status_code, 404)
+ js_data = json.loads(output.data.decode('utf-8'))
+ self.assertDictEqual(
+ js_data,
+ {
+ u'code': u'ERROR',
+ u'message': u'No git repo found with the information provided'
+ }
+ )
+
+ # Create a git repo to play with
+ gitrepo = os.path.join(self.path, 'repos', 'test.git')
+ self.assertTrue(os.path.exists(gitrepo))
+ repo = pygit2.Repository(gitrepo)
+
+ # Create a file in that git repo
+ with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
+ stream.write('foo\n bar')
+ repo.index.add('sources')
+ repo.index.write()
+
+ # Commits the files added
+ tree = repo.index.write_tree()
+ author = pygit2.Signature(
+ 'Alice Author', 'alice@authors.tld')
+ committer = pygit2.Signature(
+ 'Cecil Committer', 'cecil@committers.tld')
+ repo.create_commit(
+ 'refs/heads/master', # the name of the reference to update
+ author,
+ committer,
+ 'Add sources file for testing',
+ # binary string representing the tree object ID
+ tree,
+ # list of binary strings representing parents of the new commit
+ []
+ )
+
+ first_commit = repo.revparse_single('HEAD')
+
+ # Edit the sources file again
+ with open(os.path.join(gitrepo, 'sources'), 'w') as stream:
+ stream.write('foo\n bar\nbaz\n boose')
+ repo.index.add('sources')
+ repo.index.write()
+
+ # Commits the files added
+ tree = repo.index.write_tree()
+ author = pygit2.Signature(
+ 'Alice Author', 'alice@authors.tld')
+ committer = pygit2.Signature(
+ 'Cecil Committer', 'cecil@committers.tld')
+ repo.create_commit(
+ 'refs/heads/feature', # the name of the reference to update
+ author,
+ committer,
+ 'Add baz and boose to the sources\n\n There are more objects to '
+ 'consider',
+ # binary string representing the tree object ID
+ tree,
+ # list of binary strings representing parents of the new commit
+ [first_commit.oid.hex]
+ )
+
+ # Create another file in the master branch
+ with open(os.path.join(gitrepo, '.gitignore'), 'w') as stream:
+ stream.write('*~')
+ repo.index.add('.gitignore')
+ repo.index.write()
+
+ # Commits the files added
+ tree = repo.index.write_tree()
+ author = pygit2.Signature(
+ 'Alice Author', 'alice@authors.tld')
+ committer = pygit2.Signature(
+ 'Cecil Committer', 'cecil@committers.tld')
+ commit_hash = repo.create_commit(
+ 'refs/heads/feature_branch', # the name of the reference to update
+ author,
+ committer,
+ 'Add .gitignore file for testing',
+ # binary string representing the tree object ID
+ tree,
+ # list of binary strings representing parents of the new commit
+ [first_commit.oid.hex]
+ )
+
+ # All good
+ data = {
+ 'repo': 'test',
+ 'csrf_token': csrf_token,
+ }
+ output = self.app.post('/pv/branches/heads/', data=data)
+ self.assertEqual(output.status_code, 200)
+ js_data = json.loads(output.data.decode('utf-8'))
+ # We can't test the content since the commit hash will change all
+ # the time, so let's just check the structure
+ self.assertEqual(
+ sorted(js_data.keys()), ['branches', 'code', 'heads'])
+ self.assertEqual(js_data['code'], 'OK')
+ self.assertEqual(len(js_data['heads']), 3)
+ self.assertEqual(len(js_data['branches']), 3)
+
if __name__ == '__main__':
unittest.main(verbosity=2)