Skip to content

Commit

Permalink
Merge pull request #21 from pomalley/master
Browse files Browse the repository at this point in the history
Add git_commit_detail directive.
  • Loading branch information
OddBloke committed Apr 13, 2015
2 parents 628ed5d + b76575e commit 6b11bd2
Show file tree
Hide file tree
Showing 4 changed files with 292 additions and 8 deletions.
10 changes: 10 additions & 0 deletions docs/getting-started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,13 @@ what you want, which are outlined in the next section of the documentation.


.. _the Sphinx documentation: http://sphinx-doc.org/tutorial.html

Add Details of the Latest Commit to Your Project
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can also display information about the state of the repository when the documentation
was compiled with the ``git_commit_detail`` directive::

.. git_commit_detail::
:branch:
:commit:
48 changes: 46 additions & 2 deletions docs/using.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Using sphinx-git
================

Currently, sphinx-git provides a single extension to Sphinx: the
``git_changelog`` directive.
Currently, sphinx-git provides two extensions to Sphinx: the
``git_changelog`` and ``git_commit_detail`` directives.

git_changelog Directive
-----------------------
Expand Down Expand Up @@ -102,3 +102,47 @@ becomes:

.. [#patches]
:doc:`Patches welcome! <contributing>`
git_commit_detail Directive
---------------------------

The ``git_commit_detail`` directive produces information about the current commit in the
repository against which the documentation is being built. The following options are available:

branch
Display the branch name.

commit
Display the commit hash.

sha_length
Set the number of characters of the hash to display.

no_github_link
By default, if the repository's origin remote is GitHub, the commit will
link to the GitHub page for the commit. Use this option to disable this.

uncommitted
Show a warning if there are uncommitted changes in the repository.

untracked
Show a warning if there are untracked files in the repository directory.

For example::

.. git_commit_detail::
:branch:
:commit:
:sha_length: 10
:uncommitted:
:untracked:

becomes

.. git_commit_detail::
:branch:
:commit:
:sha_length: 10
:uncommitted:
:untracked:
94 changes: 88 additions & 6 deletions sphinx_git/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,94 @@
from sphinx.util.compat import Directive


# pylint: disable=too-few-public-methods, abstract-method
class GitDirectiveBase(Directive):
def _find_repo(self):
env = self.state.document.settings.env
repo = Repo(env.srcdir, search_parent_directories=True)
return repo


# pylint: disable=too-few-public-methods
class GitChangelog(Directive):
class GitCommitDetail(GitDirectiveBase):
default_sha_length = 7

option_spec = {
'branch': bool,
'commit': bool,
'uncommitted': bool,
'untracked': bool,
'sha_length': int,
'no_github_link': bool,
}

# pylint: disable=attribute-defined-outside-init
def run(self):
self.repo = self._find_repo()
self.branch_name = self.repo.head.ref.name
self.commit = self._get_commit()
self.sha_length = self.options.get('sha_length',
self.default_sha_length)
markup = self._build_markup()
return markup

def _get_commit(self):
repo = self._find_repo()
return repo.commit()

def _build_markup(self):
field_list = nodes.field_list()
item = nodes.paragraph()
item.append(field_list)
if 'branch' in self.options:
name = nodes.field_name(text="Branch")
body = nodes.field_body()
body.append(nodes.emphasis(text=self.branch_name))
field = nodes.field()
field += [name, body]
field_list.append(field)
if 'commit' in self.options:
name = nodes.field_name(text="Commit")
body = nodes.field_body()
if 'no_github_link' in self.options:
body.append(self._commit_text_node())
else:
body.append(self._github_link())
field = nodes.field()
field += [name, body]
field_list.append(field)
if 'uncommitted' in self.options and self.repo.is_dirty():
item.append(nodes.warning('', nodes.inline(
text="There were uncommitted changes when this was compiled."
)))
if 'untracked' in self.options and self.repo.untracked_files:
item.append(nodes.warning('', nodes.inline(
text="There were untracked files when this was compiled."
)))
return [item]

def _github_link(self):
try:
url = self.repo.remotes.origin.url
url = url.replace('.git/', '').replace('.git', '')
if 'github' in url:
commit_url = url + '/commit/' + self.commit.hexsha
ref = nodes.reference('', self.commit.hexsha[:self.sha_length],
refuri=commit_url)
par = nodes.paragraph('', '', ref)
return par
else:
return self._commit_text_node()
except AttributeError as error:
print "ERROR: ", error
return self._commit_text_node()

def _commit_text_node(self):
return nodes.emphasis(text=self.commit.hexsha[:self.sha_length])


# pylint: disable=too-few-public-methods
class GitChangelog(GitDirectiveBase):

option_spec = {
'revisions': directives.nonnegative_int,
Expand All @@ -46,11 +132,6 @@ def _commits_to_display(self):
commits = self._filter_commits(repo)
return commits

def _find_repo(self):
env = self.state.document.settings.env
repo = Repo(env.srcdir, search_parent_directories=True)
return repo

def _filter_commits(self, repo):
if 'rev-list' in self.options:
return repo.iter_commits(rev=self.options['rev-list'])
Expand Down Expand Up @@ -89,3 +170,4 @@ def _build_markup(self, commits):

def setup(app):
app.add_directive('git_changelog', GitChangelog)
app.add_directive('git_commit_detail', GitCommitDetail)
148 changes: 148 additions & 0 deletions tests/test_git_commit_detail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# -*- coding: utf-8 -*-

import os
from shutil import rmtree
from tempfile import mkdtemp, mkstemp
from BeautifulSoup import BeautifulStoneSoup

from git import Repo
from mock import Mock

from sphinx_git import GitCommitDetail

from tests.assertions import (
assert_equal,
assert_greater,
assert_less_equal,
assert_in,
assert_not_in,
assert_raises,
assert_is,
assert_is_not,
)


class TestableGitCommitDetail(GitCommitDetail):
github_nonce_url = 'https://github.com/no_user/no_repo.git/'
github_nonce_commit_base = 'https://github.com/no_user/no_repo/commit/'

def __init__(self):
self.lineno = 123
self.options = {}
self.state = Mock()


class TempDirTestCase(object):
def setup(self):
self.root = mkdtemp()
self.commit_detail = TestableGitCommitDetail()
self.commit_detail.state.document.settings.env.srcdir = self.root

def teardown(self):
rmtree(self.root)


class TestWithRepository(TempDirTestCase):
def setup(self):
super(TestWithRepository, self).setup()
self.repo = Repo.init(self.root)
self.repo.config_writer().set_value('user', 'name', 'Test User')

def test_commit_only(self):
self.repo.index.commit('my root commit')
self.commit_detail.options = {'commit': True}
nodes = self.commit_detail.run()
node_p = nodes[0] # <p> node
node_fl = node_p[0] # field list
node_f = node_fl[0] # field
assert_equal(1, len(node_fl))
assert_equal('Commit', node_f[0].astext())
assert_equal(
self.repo.commit().hexsha[:GitCommitDetail.default_sha_length],
node_f[1].astext()
)

def test_branch_only(self):
self.repo.index.commit('my root commit')
self.commit_detail.options = {'branch': True}
nodes = self.commit_detail.run()
node_p = nodes[0] # <p> node
node_fl = node_p[0] # field list
node_f = node_fl[0] # field
assert_equal(1, len(node_fl))
assert_equal('Branch', node_f[0].astext())
assert_equal('master', node_f[1].astext())

def test_commit_and_branch(self):
self.repo.index.commit('my root commit')
self.commit_detail.options = {'commit': True, 'branch': True}
nodes = self.commit_detail.run()
node_p = nodes[0] # <p> node
node_fl = node_p[0] # field list
node_f_b = node_fl[0] # field--branch
node_f_c = node_fl[1] # field--commit
assert_equal(2, len(node_fl))
assert_equal('Commit', node_f_c[0].astext())
assert_equal('Branch', node_f_b[0].astext())

def test_github_link(self):
self.repo.index.commit('my root commit')
self.commit_detail.options = {'commit': True}
self.repo.create_remote('origin', self.commit_detail.github_nonce_url)
nodes = self.commit_detail.run()
list_markup = BeautifulStoneSoup(str(nodes[0]))
assert_is_not(list_markup.reference, None)
assert_equal(
self.commit_detail.github_nonce_commit_base +
self.repo.commit().hexsha,
list_markup.reference['refuri']
)
assert_equal(
self.repo.commit().hexsha[:GitCommitDetail.default_sha_length],
list_markup.reference.text
)

def test_no_github_link(self):
self.repo.index.commit('my root commit')
self.commit_detail.options = {'commit': True, 'no_github_link': True}
self.repo.create_remote('origin', self.commit_detail.github_nonce_url)
nodes = self.commit_detail.run()
list_markup = BeautifulStoneSoup(str(nodes[0]))
assert_is(list_markup.reference, None)

def test_sha_length(self):
self.repo.index.commit('my root commit')
self.commit_detail.options = {'commit': True, 'sha_length': 4}
nodes = self.commit_detail.run()
node_p = nodes[0] # <p> node
node_fl = node_p[0] # field list
node_f = node_fl[0] # field
assert_equal(1, len(node_fl))
assert_equal('Commit', node_f[0].astext())
assert_equal(self.repo.commit().hexsha[:4], node_f[1].astext())

def test_untracked_files(self):
self.repo.index.commit('my root commit')
self.commit_detail.options = {'untracked': True}
fd, name = mkstemp(dir=self.root)
os.close(fd)
nodes = self.commit_detail.run()
node_p = nodes[0] # <p> node
assert_equal(2, len(node_p))
node_w = node_p[1] # nodes.warning
node_i = node_w[0] # inline
assert_in('untracked', node_i.astext())

def test_uncommitted_changes(self):
fd, name = mkstemp(dir=self.root)
self.repo.index.add([name])
self.repo.index.commit('my root commit')
os.write(fd, "some change")
os.close(fd)
self.commit_detail.options = {'uncommitted': True}
nodes = self.commit_detail.run()
node_p = nodes[0] # <p> node
assert_equal(2, len(node_p))
node_w = node_p[1] # nodes.warning
node_i = node_w[0] # inline
assert_in('uncommitted', node_i.astext())

0 comments on commit 6b11bd2

Please sign in to comment.