2 | #
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 | #
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import re
17 | from datetime import datetime
18 |
19 | import six
20 | from docutils import nodes
21 | from docutils.parsers.rst import Directive, directives
22 | from git import Repo
23 |
24 |
25 | # pylint: disable=too-few-public-methods, abstract-method
26 | class GitDirectiveBase(Directive):
27 | def _find_repo(self):
28 | env = self.state.document.settings.env
29 | repo_dir = self.options.get('repo-dir', env.srcdir)
30 | repo = Repo(repo_dir, search_parent_directories=True)
31 | return repo
32 |
33 |
34 | # pylint: disable=too-few-public-methods
35 | class GitCommitDetail(GitDirectiveBase):
36 | default_sha_length = 7
37 |
38 | option_spec = {
39 | 'branch': bool,
40 | 'commit': bool,
41 | 'uncommitted': bool,
42 | 'untracked': bool,
43 | 'sha_length': int,
44 | 'no_github_link': bool,
45 | }
46 |
47 | # pylint: disable=attribute-defined-outside-init
48 | def run(self):
49 | self.repo = self._find_repo()
50 | self.branch_name = None
51 | if not self.repo.head.is_detached:
52 | self.branch_name = self.repo.head.ref.name
53 | self.commit = self.repo.commit()
54 | self.sha_length = self.options.get('sha_length',
55 | self.default_sha_length)
56 | markup = self._build_markup()
57 | return markup
58 |
59 | def _build_markup(self):
60 | field_list = nodes.field_list()
61 | item = nodes.paragraph()
62 | item.append(field_list)
63 | if 'branch' in self.options and self.branch_name is not None:
64 | name = nodes.field_name(text="Branch")
65 | body = nodes.field_body()
66 | body.append(nodes.emphasis(text=self.branch_name))
67 | field = nodes.field()
68 | field += [name, body]
69 | field_list.append(field)
70 | if 'commit' in self.options:
71 | name = nodes.field_name(text="Commit")
72 | body = nodes.field_body()
73 | if 'no_github_link' in self.options:
74 | body.append(self._commit_text_node())
75 | else:
76 | body.append(self._github_link())
77 | field = nodes.field()
78 | field += [name, body]
79 | field_list.append(field)
80 | if 'uncommitted' in self.options and self.repo.is_dirty():
81 | item.append(nodes.warning('', nodes.inline(
82 | text="There were uncommitted changes when this was compiled."
83 | )))
84 | if 'untracked' in self.options and self.repo.untracked_files:
85 | item.append(nodes.warning('', nodes.inline(
86 | text="There were untracked files when this was compiled."
87 | )))
88 | return [item]
89 |
90 | def _github_link(self):
91 | try:
92 | url = self.repo.remotes.origin.url
93 | url = url.replace('.git/', '').replace('.git', '')
94 | if 'github' in url:
95 | commit_url = url + '/commit/' + self.commit.hexsha
96 | ref = nodes.reference('', self.commit.hexsha[:self.sha_length],
97 | refuri=commit_url)
98 | par = nodes.paragraph('', '', ref)
99 | return par
100 | return self._commit_text_node()
101 | except AttributeError as error:
102 | print("ERROR: ", error)
103 | return self._commit_text_node()
104 |
105 | def _commit_text_node(self):
106 | return nodes.emphasis(text=self.commit.hexsha[:self.sha_length])
107 |
108 |
109 | # pylint: disable=too-few-public-methods
110 | class GitChangelog(GitDirectiveBase):
111 |
112 | option_spec = {
113 | 'revisions': directives.nonnegative_int,
114 | 'rev-list': six.text_type,
115 | 'detailed-message-pre': bool,
116 | 'detailed-message-strong': bool,
117 | 'filename_filter': six.text_type,
118 | 'hide_author': bool,
119 | 'hide_date': bool,
120 | 'hide_details': bool,
121 | 'repo-dir': six.text_type,
122 | }
123 |
124 | def run(self):
125 | if 'rev-list' in self.options and 'revisions' in self.options:
126 | self.state.document.reporter.warning(
127 | 'Both rev-list and revisions options given; proceeding using'
128 | ' only rev-list.',
129 | line=self.lineno
130 | )
131 | commits = self._commits_to_display()
132 | markup = self._build_markup(commits)
133 | return markup
134 |
135 | def _commits_to_display(self):
136 | repo = self._find_repo()
137 | commits = self._filter_commits(repo)
138 | return commits
139 |
140 | def _filter_commits(self, repo):
141 | if 'rev-list' in self.options:
142 | commits = repo.iter_commits(rev=self.options['rev-list'])
143 | else:
144 | commits = repo.iter_commits()
145 | revisions_to_display = self.options.get('revisions', 10)
146 | commits = list(commits)[:revisions_to_display]
147 | if 'filename_filter' in self.options:
148 | return self._filter_commits_on_filenames(commits)
149 | return commits
150 |
151 | def _filter_commits_on_filenames(self, commits):
152 | filtered_commits = []
153 | filter_exp = re.compile(self.options.get('filename_filter', r'.*'))
154 | for commit in commits:
155 | # SHA of an empty tree found at
156 | # http://stackoverflow.com/questions/33916648/get-the-diff-details-of-first-commit-in-gitpython
157 | # will be used to get the list of files of initial commit
158 | compared_with = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'
159 | if len(commit.parents) > 0: # pylint: disable=len-as-condition
160 | compared_with = commit.parents[0].hexsha
161 | for diff in commit.diff(compared_with):
162 | if filter_exp.match(diff.a_path) or \
163 | filter_exp.match(diff.b_path):
164 | filtered_commits.append(commit)
165 | break
166 | return filtered_commits
167 |
168 | def _build_markup(self, commits):
169 | list_node = nodes.bullet_list()
170 | for commit in commits:
171 | date_str = datetime.fromtimestamp(commit.authored_date)
172 | if '\n' in commit.message:
173 | message, detailed_message = commit.message.split('\n', 1)
174 | else:
175 | message = commit.message
176 | detailed_message = None
177 |
178 | item = nodes.list_item()
179 | par = nodes.paragraph()
180 | # choose detailed message style by detailed-message-strong option
181 | if self.options.get('detailed-message-strong', True):
182 | par += nodes.strong(text=message)
183 | else:
184 | par += nodes.inline(text=message)
185 |
186 | if not self.options.get('hide_author'):
187 | par += [nodes.inline(text=" by "),
188 | nodes.emphasis(text=six.text_type(commit.author))]
189 | if not self.options.get('hide_date'):
190 | par += [nodes.inline(text=" at "),
191 | nodes.emphasis(text=str(date_str))]
192 | item.append(par)
193 | if detailed_message and not self.options.get('hide_details'):
194 | detailed_message = detailed_message.strip()
195 | if self.options.get('detailed-message-pre', False):
196 | item.append(
197 | nodes.literal_block(text=detailed_message))
198 | else:
199 | item.append(nodes.paragraph(text=detailed_message))
200 | list_node.append(item)
201 | return [list_node]
202 |
203 |
204 | def setup(app):
205 | app.add_directive('git_changelog', GitChangelog)
206 | app.add_directive('git_commit_detail', GitCommitDetail)
207 |
--------------------------------------------------------------------------------
/sphinx_git/version.py:
--------------------------------------------------------------------------------
1 | __version__ = '11.0.0'
2 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 | from shutil import rmtree
2 | from tempfile import mkdtemp
3 |
4 | from mock import Mock
5 |
6 |
7 | class TempDirTestCase(object):
8 | def setup(self):
9 | self.root = mkdtemp()
10 |
11 | def teardown(self):
12 | rmtree(self.root)
13 |
14 |
15 | class MakeTestableMixin(object):
16 | """
17 | Define an __init__ with no arguments for sphinx directives.
18 |
19 | This saves us from having to pass in a bunch of Mocks which we will never
20 | look at.
21 | """
22 |
23 | def __init__(self):
24 | self.lineno = 123
25 | self.options = {}
26 | self.state = Mock()
27 |
--------------------------------------------------------------------------------
/tests/test_git_changelog.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import os
3 | from datetime import datetime
4 |
5 | import six
6 | from bs4 import BeautifulSoup
7 | from git import InvalidGitRepositoryError, Repo
8 | from mock import ANY, call
9 | from nose.tools import (
10 | assert_equal,
11 | assert_greater,
12 | assert_in,
13 | assert_less_equal,
14 | assert_not_in,
15 | assert_raises,
16 | )
17 |
18 | from sphinx_git import GitChangelog
19 |
20 | from . import MakeTestableMixin, TempDirTestCase
21 |
22 |
23 | class TestableGitChangelog(MakeTestableMixin, GitChangelog):
24 |
25 | pass
26 |
27 |
28 | class ChangelogTestCase(TempDirTestCase):
29 |
30 | def setup(self):
31 | super(ChangelogTestCase, self).setup()
32 | self.changelog = TestableGitChangelog()
33 | self.changelog.state.document.settings.env.srcdir = self.root
34 |
35 |
36 | class TestNoRepository(ChangelogTestCase):
37 |
38 | def test_not_a_repository(self):
39 | assert_raises(InvalidGitRepositoryError, self.changelog.run)
40 |
41 |
42 | class TestWithRepository(ChangelogTestCase):
43 |
44 | def _set_username(self, username):
45 | config_writer = self.repo.config_writer()
46 | config_writer.set_value('user', 'name', username)
47 | config_writer.release()
48 |
49 | def setup(self):
50 | super(TestWithRepository, self).setup()
51 | self.repo = Repo.init(self.root)
52 | self._set_username('Test User')
53 |
54 | def test_no_commits(self):
55 | assert_raises(ValueError, self.changelog.run)
56 |
57 | def test_single_commit_produces_single_item(self):
58 | self.repo.index.commit('my root commit')
59 | nodes = self.changelog.run()
60 | assert_equal(1, len(nodes))
61 | list_markup = BeautifulSoup(str(nodes[0]), features='xml')
62 | assert_equal(1, len(list_markup.findAll('bullet_list')))
63 | bullet_list = list_markup.bullet_list
64 | assert_equal(1, len(bullet_list.findAll('list_item')))
65 |
66 | def test_single_commit_message_and_user_display(self):
67 | self.repo.index.commit('my root commit')
68 | nodes = self.changelog.run()
69 | list_markup = BeautifulSoup(str(nodes[0]), features='xml')
70 | item = list_markup.bullet_list.list_item
71 | children = list(item.childGenerator())
72 | assert_equal(1, len(children))
73 | par_children = list(item.paragraph.childGenerator())
74 | assert_equal(5, len(par_children))
75 | assert_equal('my root commit', par_children[0].text)
76 | assert_equal('Test User', par_children[2].text)
77 |
78 | def test_single_commit_message_and_user_display_with_non_ascii_chars(self):
79 | self._set_username('þéßþ Úßéë')
80 | self.repo.index.commit('my root commit')
81 | nodes = self.changelog.run()
82 | list_markup = BeautifulSoup(six.text_type(nodes[0]), features='xml')
83 | item = list_markup.bullet_list.list_item
84 | children = list(item.childGenerator())
85 | assert_equal(1, len(children))
86 | par_children = list(item.paragraph.childGenerator())
87 | assert_equal(5, len(par_children))
88 | assert_equal('my root commit', par_children[0].text)
89 | assert_equal(u'þéßþ Úßéë', par_children[2].text)
90 |
91 | def test_single_commit_time_display(self):
92 | before = datetime.now().replace(microsecond=0)
93 | self.repo.index.commit('my root commit')
94 | nodes = self.changelog.run()
95 | after = datetime.now()
96 | list_markup = BeautifulSoup(str(nodes[0]), features='xml')
97 | item = list_markup.bullet_list.list_item.paragraph
98 | children = list(item.childGenerator())
99 | timestamp = datetime.strptime(children[4].text, '%Y-%m-%d %H:%M:%S')
100 | assert_less_equal(before, timestamp)
101 | assert_greater(after, timestamp)
102 |
103 | def test_single_commit_default_detail_setting(self):
104 | self.repo.index.commit(
105 | 'my root commit\n\nadditional information\nmore info'
106 | )
107 | nodes = self.changelog.run()
108 | list_markup = BeautifulSoup(str(nodes[0]), features='xml')
109 | item = list_markup.bullet_list.list_item
110 | children = list(item.childGenerator())
111 | assert_equal(2, len(children))
112 | par_children = list(item.paragraph.childGenerator())
113 | assert_equal(5, len(par_children))
114 | assert_equal('my root commit', par_children[0].text)
115 | assert_equal('Test User', par_children[2].text)
116 | assert_equal(
117 | str(children[1]),
118 | 'additional information\nmore info'
119 | )
120 |
121 | def test_single_commit_preformmated_detail_lines(self):
122 | self.repo.index.commit(
123 | 'my root commit\n\nadditional information\nmore info'
124 | )
125 | self.changelog.options.update({'detailed-message-pre': True})
126 | nodes = self.changelog.run()
127 | list_markup = BeautifulSoup(str(nodes[0]), features='xml')
128 | item = list_markup.bullet_list.list_item
129 | children = list(item.childGenerator())
130 | assert_equal(2, len(children))
131 | assert_equal(
132 | str(children[1]),
133 | 'additional information\n'
134 | 'more info'
135 | )
136 |
137 | def test_more_than_ten_commits(self):
138 | for n in range(15):
139 | self.repo.index.commit('commit #{0}'.format(n))
140 | nodes = self.changelog.run()
141 | assert_equal(1, len(nodes))
142 | list_markup = BeautifulSoup(str(nodes[0]), features='xml')
143 | assert_equal(1, len(list_markup.findAll('bullet_list')))
144 | bullet_list = list_markup.bullet_list
145 | assert_equal(10, len(bullet_list.findAll('list_item')))
146 | for n, child in zip(range(15, 5), bullet_list.childGenerator()):
147 | assert_in('commit #{0}'.format(n), child.text)
148 | assert_not_in('commit #4', bullet_list.text)
149 |
150 | def test_specifying_number_of_commits(self):
151 | for n in range(15):
152 | self.repo.index.commit('commit #{0}'.format(n))
153 | self.changelog.options.update({'revisions': 5})
154 | nodes = self.changelog.run()
155 | assert_equal(1, len(nodes))
156 | list_markup = BeautifulSoup(str(nodes[0]), features='xml')
157 | assert_equal(1, len(list_markup.findAll('bullet_list')))
158 | bullet_list = list_markup.bullet_list
159 | assert_equal(5, len(bullet_list.findAll('list_item')))
160 | for n, child in zip(range(15, 10), bullet_list.childGenerator()):
161 | assert_in('commit #{0}'.format(n), child.text)
162 | assert_not_in('commit #9', bullet_list.text)
163 |
164 | def test_specifying_a_rev_list(self):
165 | self.repo.index.commit('before tag')
166 | commit = self.repo.index.commit('at tag')
167 | self.repo.index.commit('after tag')
168 | self.repo.index.commit('last commit')
169 | self.repo.create_tag('testtag', commit)
170 |
171 | self.changelog.options.update({'rev-list': 'testtag..'})
172 | nodes = self.changelog.run()
173 |
174 | assert_equal(1, len(nodes))
175 | list_markup = BeautifulSoup(str(nodes[0]), features='xml')
176 | assert_equal(1, len(list_markup.findAll('bullet_list')))
177 |
178 | bullet_list = list_markup.bullet_list
179 | assert_equal(2, len(bullet_list.findAll('list_item')))
180 |
181 | children = list(bullet_list.childGenerator())
182 | first_element = children[0]
183 | second_element = children[1]
184 | assert_in('last commit', first_element.text)
185 | assert_in('after tag', second_element.text)
186 |
187 | def test_warning_given_if_rev_list_and_revisions_both_given(self):
188 | self.repo.index.commit('a commit')
189 | self.changelog.options.update({'rev-list': 'HEAD', 'revisions': 12})
190 | nodes = self.changelog.run()
191 | assert_equal(
192 | 1, self.changelog.state.document.reporter.warning.call_count
193 | )
194 |
195 | def test_line_number_displayed_in_multiple_option_warning(self):
196 | self.repo.index.commit('a commit')
197 | self.changelog.options.update({'rev-list': 'HEAD', 'revisions': 12})
198 | nodes = self.changelog.run()
199 | document_reporter = self.changelog.state.document.reporter
200 | assert_equal(
201 | [call(ANY, line=self.changelog.lineno)],
202 | document_reporter.warning.call_args_list
203 | )
204 |
205 | def test_name_filter(self):
206 | self.repo.index.commit('initial')
207 | for file_name in ['abc.txt', 'bcd.txt', 'abc.other', 'atxt']:
208 | full_path = os.path.join(self.repo.working_tree_dir, file_name)
209 | f = open(full_path, 'w+')
210 | f.close()
211 | self.repo.index.add([full_path])
212 | self.repo.index.commit('commit with file {}'.format(file_name))
213 | self.repo.index.commit('commit without file')
214 |
215 | self.changelog.options.update({'filename_filter': 'a.*txt'})
216 | nodes = self.changelog.run()
217 | assert_equal(1, len(nodes))
218 | list_markup = BeautifulSoup(str(nodes[0]), features='xml')
219 | assert_equal(1, len(list_markup.findAll('bullet_list')))
220 |
221 | bullet_list = list_markup.bullet_list
222 | assert_equal(2, len(bullet_list.findAll('list_item')), nodes)
223 |
224 | next_file = os.path.join(self.repo.working_tree_dir, 'atxt')
225 | f = open(next_file, 'w+')
226 | f.close()
227 | self.repo.index.add([next_file])
228 | self.repo.index.commit('show me')
229 |
230 | nodes = self.changelog.run()
231 | assert_equal(1, len(nodes), nodes)
232 | list_markup = BeautifulSoup(str(nodes[0]), features='xml')
233 | assert_equal(1, len(list_markup.findAll('bullet_list')))
234 |
235 | bullet_list = list_markup.bullet_list
236 | assert_equal(2, len(bullet_list.findAll('list_item')), nodes)
237 |
238 | def test_single_commit_hide_details(self):
239 | self.repo.index.commit(
240 | 'Another commit\n\nToo much information'
241 | )
242 | self.changelog.options.update({'hide_details': True})
243 | nodes = self.changelog.run()
244 | list_markup = BeautifulSoup(str(nodes[0]), features='xml')
245 | item = list_markup.bullet_list.list_item
246 | children = list(item.childGenerator())
247 | assert_equal(1, len(children))
248 | par_children = list(item.paragraph.childGenerator())
249 | assert_equal(5, len(par_children))
250 | assert_equal('Another commit', par_children[0].text)
251 | assert_equal('Test User', par_children[2].text)
252 |
253 | def test_single_commit_message_hide_author(self):
254 | self.repo.index.commit('Yet another commit')
255 | self.changelog.options.update({'hide_author': True})
256 | nodes = self.changelog.run()
257 | list_markup = BeautifulSoup(str(nodes[0]), features='xml')
258 | item = list_markup.bullet_list.list_item
259 | children = list(item.childGenerator())
260 | print(children)
261 | assert_equal(1, len(children))
262 | par_children = list(item.paragraph.childGenerator())
263 | assert_equal(3, len(par_children))
264 | assert_equal('Yet another commit', par_children[0].text)
265 | assert_not_in(' by Test User', par_children[1].text)
266 | assert_in(' at ', par_children[1].text)
267 |
268 | def test_single_commit_message_hide_date(self):
269 | self.repo.index.commit('Yet yet another commit')
270 | self.changelog.options.update({'hide_date': True})
271 | nodes = self.changelog.run()
272 | list_markup = BeautifulSoup(str(nodes[0]), features='xml')
273 | item = list_markup.bullet_list.list_item
274 | children = list(item.childGenerator())
275 | print(children)
276 | assert_equal(1, len(children))
277 | par_children = list(item.paragraph.childGenerator())
278 | assert_equal(3, len(par_children))
279 | assert_equal('Yet yet another commit', par_children[0].text)
280 | assert_not_in(' at ', par_children[1].text)
281 | assert_in(' by ', par_children[1].text)
282 |
283 |
284 | class TestWithOtherRepository(TestWithRepository):
285 | """
286 | The destination repository is not in the same repository as the rst files.
287 | """
288 | def setup(self):
289 | super(TestWithOtherRepository, self).setup()
290 | self.changelog.state.document.settings.env.srcdir = os.getcwd()
291 | self.changelog.options.update({'repo-dir': self.root})
292 |
--------------------------------------------------------------------------------
/tests/test_git_commit_detail.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import os
3 | from tempfile import mkstemp
4 |
5 | from bs4 import BeautifulSoup
6 | from git import Repo
7 | from nose.tools import assert_equal, assert_in, assert_is, assert_is_not
8 |
9 | from sphinx_git import GitCommitDetail
10 |
11 | from . import MakeTestableMixin, TempDirTestCase
12 |
13 |
14 | class TestableGitCommitDetail(MakeTestableMixin, GitCommitDetail):
15 | github_nonce_url = 'https://github.com/no_user/no_repo.git/'
16 | github_nonce_commit_base = 'https://github.com/no_user/no_repo/commit/'
17 |
18 |
19 | class TestCommitDetail(TempDirTestCase):
20 | def setup(self):
21 | super(TestCommitDetail, self).setup()
22 | self.commit_detail = TestableGitCommitDetail()
23 | self.commit_detail.state.document.settings.env.srcdir = self.root
24 | self.repo = Repo.init(self.root)
25 | config_writer = self.repo.config_writer()
26 | config_writer.set_value('user', 'name', 'Test User')
27 | config_writer.release()
28 |
29 | def test_commit_only(self):
30 | self.repo.index.commit('my root commit')
31 | self.commit_detail.options = {'commit': True}
32 | nodes = self.commit_detail.run()
33 | node_p = nodes[0] # node
34 | node_fl = node_p[0] # field list
35 | node_f = node_fl[0] # field
36 | assert_equal(1, len(node_fl))
37 | assert_equal('Commit', node_f[0].astext())
38 | assert_equal(
39 | self.repo.commit().hexsha[:GitCommitDetail.default_sha_length],
40 | node_f[1].astext()
41 | )
42 |
43 | def test_branch_only(self):
44 | self.repo.index.commit('my root commit')
45 | self.commit_detail.options = {'branch': True}
46 | nodes = self.commit_detail.run()
47 | node_p = nodes[0] #
node
48 | node_fl = node_p[0] # field list
49 | node_f = node_fl[0] # field
50 | assert_equal(1, len(node_fl))
51 | assert_equal('Branch', node_f[0].astext())
52 | assert_equal('master', node_f[1].astext())
53 |
54 | def test_commit_and_branch(self):
55 | self.repo.index.commit('my root commit')
56 | self.commit_detail.options = {'commit': True, 'branch': True}
57 | nodes = self.commit_detail.run()
58 | node_p = nodes[0] #
node
59 | node_fl = node_p[0] # field list
60 | node_f_b = node_fl[0] # field--branch
61 | node_f_c = node_fl[1] # field--commit
62 | assert_equal(2, len(node_fl))
63 | assert_equal('Commit', node_f_c[0].astext())
64 | assert_equal('Branch', node_f_b[0].astext())
65 |
66 | def test_github_link(self):
67 | self.repo.index.commit('my root commit')
68 | self.commit_detail.options = {'commit': True}
69 | self.repo.create_remote('origin', self.commit_detail.github_nonce_url)
70 | nodes = self.commit_detail.run()
71 | list_markup = BeautifulSoup(str(nodes[0]), features='xml')
72 | assert_is_not(list_markup.reference, None)
73 | assert_equal(
74 | self.commit_detail.github_nonce_commit_base +
75 | self.repo.commit().hexsha,
76 | list_markup.reference['refuri']
77 | )
78 | assert_equal(
79 | self.repo.commit().hexsha[:GitCommitDetail.default_sha_length],
80 | list_markup.reference.text
81 | )
82 |
83 | def test_no_github_link(self):
84 | self.repo.index.commit('my root commit')
85 | self.commit_detail.options = {'commit': True, 'no_github_link': True}
86 | self.repo.create_remote('origin', self.commit_detail.github_nonce_url)
87 | nodes = self.commit_detail.run()
88 | list_markup = BeautifulSoup(str(nodes[0]), features='xml')
89 | assert_is(list_markup.reference, None)
90 |
91 | def test_sha_length(self):
92 | self.repo.index.commit('my root commit')
93 | self.commit_detail.options = {'commit': True, 'sha_length': 4}
94 | nodes = self.commit_detail.run()
95 | node_p = nodes[0] #
node
96 | node_fl = node_p[0] # field list
97 | node_f = node_fl[0] # field
98 | assert_equal(1, len(node_fl))
99 | assert_equal('Commit', node_f[0].astext())
100 | assert_equal(self.repo.commit().hexsha[:4], node_f[1].astext())
101 |
102 | def test_untracked_files(self):
103 | self.repo.index.commit('my root commit')
104 | self.commit_detail.options = {'untracked': True}
105 | fd, name = mkstemp(dir=self.root)
106 | os.close(fd)
107 | nodes = self.commit_detail.run()
108 | node_p = nodes[0] #
node
109 | assert_equal(2, len(node_p))
110 | node_w = node_p[1] # nodes.warning
111 | node_i = node_w[0] # inline
112 | assert_in('untracked', node_i.astext())
113 |
114 | def test_uncommitted_changes(self):
115 | fd, name = mkstemp(dir=self.root)
116 | self.repo.index.add([name])
117 | self.repo.index.commit('my root commit')
118 | os.write(fd, "some change".encode('utf-8'))
119 | os.close(fd)
120 | self.commit_detail.options = {'uncommitted': True}
121 | nodes = self.commit_detail.run()
122 | node_p = nodes[0] #
node
123 | assert_equal(2, len(node_p))
124 | node_w = node_p[1] # nodes.warning
125 | node_i = node_w[0] # inline
126 | assert_in('uncommitted', node_i.astext())
127 |
128 | def test_detached_head(self):
129 | self.repo.index.commit('my root commit')
130 | self.repo.index.commit('a second commit')
131 | self.repo.head.reference = self.repo.commit('HEAD~')
132 | assert self.repo.head.is_detached, "HEAD unexpectedly attached"
133 |
134 | self.commit_detail.options = {'commit': True}
135 | nodes = self.commit_detail.run()
136 | node_p = nodes[0] #
node
137 | node_fl = node_p[0] # field list
138 | node_f = node_fl[0] # field
139 | assert_equal(1, len(node_fl))
140 | assert_equal('Commit', node_f[0].astext())
141 | assert_equal(
142 | self.repo.commit().hexsha[:GitCommitDetail.default_sha_length],
143 | node_f[1].astext()
144 | )
145 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py27,py34,py35
3 |
4 | [testenv]
5 | deps = -rrequirements.txt
6 | commands =
7 | pycodestyle sphinx_git/ tests/
8 | pylint --rcfile=.pylintrc sphinx_git/
9 | isort --diff --recursive --check sphinx_git tests
10 | nosetests
11 |
--------------------------------------------------------------------------------