Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
dev
---

* Add git_changelog_by_date directive (Emmanuelle Delescolle)
* Add filename_filter argument to git_changelog (Emmanuelle Delescolle)

v10.0.0
Expand Down
21 changes: 21 additions & 0 deletions docs/using.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,27 @@ list. If a commit has a detailed message (i.e. any part of the commit message
that is not on the first line), that will be output below the list item for
that commit.

git_changelog_by_date Directive
-------------------------------

The ``git_changelog_by_date`` directive produces a list of commits in the
repository in which the documentation build is happening, grouped and ordered
by date. This is especially useful when using features like `rebase`

``git_changelog_by_date`` has the same behaviour and options as
``git_changelog``.

So::

.. git_changelog_by_date::

produces:

.. git_changelog_by_date::

As you can see, the top-most list is now a list of date. The commit date has
also been removed from each's commit details as it is redundant.

Changing Number of Revisions in Output
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
57 changes: 57 additions & 0 deletions sphinx_git/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,63 @@ def _build_markup(self, commits):
return [list_node]


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

# pylint: disable=no-self-use
def _add_date_node(self, dates_node, date_node, date_list_node):
if date_list_node is not None and date_node is not None:
date_node.append(date_list_node)
dates_node.append(date_node)

def _build_markup(self, commits):
dates_node = nodes.bullet_list()
date_node = None
date_list_node = None
cur_date = None

# commits are not always given in chronological order,
# especially when using rebase
commits.sort(key=lambda c: c.authored_date, reverse=True)

for commit in commits:
date_str = '{}'.format(
datetime.fromtimestamp(commit.authored_date)
).split(' ')[0]
if date_str != cur_date:
self._add_date_node(dates_node, date_node, date_list_node)
date_node = nodes.list_item()
date_node.append(nodes.strong(text="On {}".format(date_str)))
date_list_node = nodes.bullet_list()
cur_date = date_str

if '\n' in commit.message:
message, detailed_message = commit.message.split('\n', 1)
else:
message = commit.message
detailed_message = None

item = nodes.list_item()
item += [
nodes.strong(text=message),
nodes.inline(text=" by "),
nodes.emphasis(text=six.text_type(commit.author)),
]
if detailed_message:
detailed_message = detailed_message.strip()
if self.options.get('detailed-message-pre', False):
item.append(
nodes.literal_block(text=detailed_message))
else:
item.append(nodes.paragraph(text=detailed_message))
date_list_node.append(item)

self._add_date_node(dates_node, date_node, date_list_node)

return [dates_node]


def setup(app):
app.add_directive('git_changelog', GitChangelog)
app.add_directive('git_changelog_by_date', GitChangelogByDate)
app.add_directive('git_commit_detail', GitCommitDetail)
90 changes: 90 additions & 0 deletions tests/test_git_changelog_by_date.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import os
from datetime import datetime, timedelta

from bs4 import BeautifulSoup
from git import Repo

from nose.tools import (
assert_equal,
)

from . import MakeTestableMixin, TempDirTestCase
from sphinx_git import GitChangelogByDate


class TestableGitChangelogByDate(MakeTestableMixin, GitChangelogByDate):

pass


class ChangelogByDateTestCase(TempDirTestCase):

def _set_username(self, username):
config_writer = self.repo.config_writer()
config_writer.set_value('user', 'name', username)
config_writer.release()

def setup(self):
super(ChangelogByDateTestCase, self).setup()
self.changelog = TestableGitChangelogByDate()
self.changelog.state.document.settings.env.srcdir = self.root
self.repo = Repo.init(self.root)
self._set_username('Test User')


class TestGroupByDate(ChangelogByDateTestCase):
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests look good, but I'd also like to see one for the rebase/cherry-pick case, checking that the ordering is as we expect it to be.


def test_single_commit_produces_single_item(self):
self.repo.index.commit('my root commit')
nodes = self.changelog.run()
assert_equal(1, len(nodes))
list_markup = BeautifulSoup(str(nodes[0]), features='xml')
# One bullet_list for the dates and one for the commits on that
# unique date
assert_equal(2, len(list_markup.findAll('bullet_list')))
l = list_markup.bullet_list
# One list_item for the date and one for the commit on that date
assert_equal(2, len(l.findAll('list_item')))

def test_commits_on_same_date_produce_single_item(self):
non_root_commits_count = 5
self.repo.index.commit('my root commit')
for i in range(non_root_commits_count):
self.repo.index.commit('commit #{}'.format(i+1))

nodes = self.changelog.run()
assert_equal(1, len(nodes))
list_markup = BeautifulSoup(str(nodes[0]), features='xml')
# One bullet_list for the dates and one for the commits on that
# unique date
assert_equal(2, len(list_markup.findAll('bullet_list')))
l = list_markup.bullet_list
# One list_item for the date and one per commit on that date
assert_equal(2 + non_root_commits_count, len(l.findAll('list_item')))

def test_commits_on_different_dates_produce_one_item_per_date(self):
non_root_commits_count = 5
self.repo.index.commit('my root commit')
for i in range(non_root_commits_count):
self.repo.index.commit('commit #{}'.format(i + 1))

# change date
tomorrow = '{}'.format(
datetime.now() + timedelta(days=1)
).split('.')[0]
os.environ['GIT_AUTHOR_DATE'] = tomorrow
os.environ['GIT_COMMITER_DATE'] = tomorrow
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect the modifying the environment wholesale might cause problems for other tests that run after this one, if they rely on the commit date being today. Could we modify this to restore the environment to its previous state?

for i in range(non_root_commits_count):
self.repo.index.commit('commit #{}'.format(
i + 1 + non_root_commits_count
))

nodes = self.changelog.run()
assert_equal(1, len(nodes))
list_markup = BeautifulSoup(str(nodes[0]), features='xml')
# One bullet_list for the dates and one for the commits on each date
assert_equal(3, len(list_markup.findAll('bullet_list')))
l = list_markup.bullet_list
# One list_item for each date and one per commit on that date
assert_equal(2 + 2 * non_root_commits_count,
len(l.findAll('list_item')))