From 034c4aa02c1541fafdc5a0ddaaf8fd92ceb1485f Mon Sep 17 00:00:00 2001 From: Emmanuelle Delescolle Date: Tue, 17 May 2016 11:51:20 +0200 Subject: [PATCH 1/2] Add git_changlog_by_date directive + tests + docs --- CHANGELOG | 1 + docs/using.rst | 21 +++++++ sphinx_git/__init__.py | 57 ++++++++++++++++++ tests/test_git_changelog_by_date.py | 90 +++++++++++++++++++++++++++++ 4 files changed, 169 insertions(+) create mode 100644 tests/test_git_changelog_by_date.py diff --git a/CHANGELOG b/CHANGELOG index a283583..1166183 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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 diff --git a/docs/using.rst b/docs/using.rst index da5fb43..b828fa0 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -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 inwhich 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/sphinx_git/__init__.py b/sphinx_git/__init__.py index acedcc0..d732b80 100644 --- a/sphinx_git/__init__.py +++ b/sphinx_git/__init__.py @@ -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) diff --git a/tests/test_git_changelog_by_date.py b/tests/test_git_changelog_by_date.py new file mode 100644 index 0000000..d6618cd --- /dev/null +++ b/tests/test_git_changelog_by_date.py @@ -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): + + 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 + 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'))) From d1c7b5fe47eb13559aec5ffcb5133d6b2402e7e6 Mon Sep 17 00:00:00 2001 From: Emmanuelle Delescolle Date: Tue, 17 May 2016 11:58:55 +0200 Subject: [PATCH 2/2] typo fix --- docs/using.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/using.rst b/docs/using.rst index b828fa0..997680a 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -27,7 +27,7 @@ git_changelog_by_date Directive ------------------------------- The ``git_changelog_by_date`` directive produces a list of commits in the -repository inwhich the documentation build is happening, grouped and ordered +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