Skip to content

Commit c88c3a0

Browse files
authored
Merge pull request #8 from sphinx-notes/fix/pathspec
path and current_doc fixes
2 parents c2e344c + 0596323 commit c88c3a0

3 files changed

Lines changed: 47 additions & 64 deletions

File tree

docs/usage.rst

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,22 @@ following parameters are available:
1616
``count``
1717
Number of recent revisions to return (default from :confval:`recentupdate_count`).
1818

19-
``path``
20-
A git pathspec (:manpage:`gitglossary(7)`) to filter file changes
21-
(default ``'.'``).
19+
``paths``
20+
A list of git pathspecs (:manpage:`gitglossary(7)`) to filter file changes
21+
(default ``['.']``).
2222
See also :example:`Recent Updates of Custom Path`.
2323

2424
``current_doc``
2525
If ``True``, only return revisions that modified the current document
26-
(default ``False``).
26+
(default ``False``). When enabled, ``paths`` is overridden with a pathspec
27+
matching the current document.
2728
See also :example:`Recent Updates to Current Document`.
2829

30+
.. note::
31+
32+
``paths`` and ``current_doc`` are mutually exclusive. When ``current_doc=True``,
33+
the ``paths`` parameter is ignored.
34+
2935
.. role:: py(code)
3036
:language: Python
3137

@@ -89,7 +95,7 @@ Examples
8995
9096
Recent changes of the ``docs/index.rst`` file:
9197
92-
{% for r in load_extra('recentupdate', count=5, path='docs/index.rst') %}
98+
{% for r in load_extra('recentupdate', count=5, paths=['docs/index.rst']) %}
9399
``{{ r.date }}`` — {{ r.message[0] }}
94100
{% endfor %}
95101

src/sphinxnotes/recentupdate/__init__.py

Lines changed: 35 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,11 @@
1414
from dataclasses import dataclass
1515
from collections import OrderedDict
1616
from os import path
17-
from pathlib import Path
1817
from itertools import islice
1918

2019
from git import Repo
2120

2221
from sphinx.util import logging
23-
from sphinx.util.matching import Matcher
2422
from sphinx.config import ENUM
2523

2624
from sphinxnotes.render import (
@@ -48,16 +46,6 @@ class Revision:
4846
#: Git commit author date
4947
date: datetime
5048

51-
# FYI, possible status letters are:
52-
# :A: addition of a file
53-
# :C: copy of a file into a new one
54-
# :D: deletion of a file
55-
# :M: modification of the contents or mode of a file
56-
# :R: renaming of a file
57-
# :T: change in the type of the file
58-
# :U: file is unmerged (you must complete the merge before it can be committed)
59-
# :X: "unknown" change type (most probably a bug, please report it)
60-
6149
#: List of docname, corresponding to files which are newly added
6250
added_docs: list[str]
6351
#: List of docname, corresponding to files which are modified
@@ -135,11 +123,10 @@ def compact_groups(
135123
def get_git_revisions(
136124
repo: Repo,
137125
env: BuildEnvironment,
138-
path: str,
139-
current_doc: str | None = None,
126+
paths: list[str],
140127
) -> Iterator[Revision]:
141128
"""Yield Revision objects from git commits."""
142-
for cur in repo.iter_commits(paths=path):
129+
for cur in repo.iter_commits(paths=paths):
143130
matches = [x in cur.message for x in env.config.recentupdate_exclude_commit]
144131
if any(matches):
145132
logger.debug(
@@ -155,37 +142,42 @@ def get_git_revisions(
155142
for blob in cur.tree.traverse():
156143
if blob.type != 'blob':
157144
continue
158-
docname = path2docname(repo, env, blob.path)
145+
docname = path2doc(repo, env, blob.path)
159146
if docname is None:
160147
continue
161148
a.append(docname)
162149
else:
163-
diff_idx = prev.tree.diff(cur)
164-
for diff in diff_idx:
165-
if diff.a_path is None:
150+
# Possible status letters are:
151+
# :A: addition of a file
152+
# :C: copy of a file into a new one
153+
# :D: deletion of a file
154+
# :M: modification of the contents or mode of a file
155+
# :R: renaming of a file
156+
# :T: change in the type of the file
157+
# :U: file is unmerged (you must complete the merge before it can be committed)
158+
# :X: "unknown" change type (most probably a bug, please report it)
159+
status_maps = {'M': m, 'A': a, 'D': d }
160+
161+
# Use git diff --name-status with pathspecs for native pathspec matching
162+
name_status = repo.git.diff(
163+
prev.hexsha, cur.hexsha, '--name-status', '--', *paths
164+
)
165+
for line in name_status.splitlines():
166+
if not line.strip():
166167
continue
167-
docname = path2docname(repo, env, diff.a_path)
168+
status, file_path = line.split('\t', 1)
169+
docname = path2doc(repo, env, file_path)
168170
if docname is None:
169171
continue
170172

171-
if diff.change_type == 'M':
172-
m.append(docname)
173-
elif diff.change_type == 'A':
174-
a.append(docname)
175-
elif diff.change_type == 'D':
176-
d.append(docname)
173+
if status in status_maps:
174+
status_maps[status].append(docname)
177175
else:
178-
logger.info(
179-
f'Skip {diff.a_path}: '
180-
f'unsupported change type {diff.change_type}'
181-
)
176+
logger.info(f'Skip {file_path}: unsupported change type {status}')
182177

183178
if len(m) + len(a) + len(d) == 0:
184179
logger.debug(f'Skip commit {cur.hexsha}: no document changes')
185180
continue
186-
if current_doc is not None and current_doc not in (m + a + d):
187-
logger.debug(f'Skip commit {cur.hexsha}: no changes to {current_doc}')
188-
continue
189181

190182
yield Revision(
191183
message=str(cur.message).splitlines(),
@@ -197,28 +189,9 @@ def get_git_revisions(
197189
)
198190

199191

200-
def path2docname(repo: Repo, env: BuildEnvironment, file: str) -> str | None:
201-
"""Convert a repo-relative file path to a Sphinx docname."""
202-
relsrcdir_to_repo = path.relpath(env.srcdir, repo.working_dir)
203-
relfn_to_srcdir = path.relpath(file, relsrcdir_to_repo)
204-
absfn = Path(repo.working_dir, file)
205-
if not absfn.is_relative_to(env.srcdir):
206-
logger.debug(f'Skip {file}: out of srcdir')
207-
return None
208-
209-
excluded = Matcher(env.config.exclude_patterns)
210-
if excluded(relfn_to_srcdir):
211-
logger.debug(f'Skip {file}: excluded by exclude_patterns')
212-
return None
213-
214-
docname, ext = path.splitext(relfn_to_srcdir)
215-
source_suffix = list(env.config.source_suffix.keys())
216-
if not ext or ext not in source_suffix:
217-
logger.debug(f'Skip {file}: not {source_suffix} files')
218-
return None
219-
220-
logger.debug(f'Get docname: {docname}')
221-
return docname
192+
def path2doc(repo: Repo, env: BuildEnvironment, blob_path: str) -> str | None:
193+
"""Convert a git repo-relative blob path to a Sphinx document name. """
194+
return env.path2doc(path.join(repo.working_dir, blob_path))
222195

223196

224197
@extra_context('recentupdate')
@@ -232,15 +205,19 @@ def generate(
232205
self,
233206
req: ExtraContextRequest,
234207
count: int = 0,
235-
path: str = '.',
208+
paths: list[str] = ['.', ],
236209
current_doc: bool = False,
237210
group_by: str = '',
238211
) -> Any:
239212
count = count or req.env.config.recentupdate_count
240213
group_by = group_by or req.env.config.recentupdate_group_by
241-
docname = req.env.docname if current_doc else None
242214

243-
git_revs = get_git_revisions(self.repo, req.env, path, docname)
215+
if current_doc:
216+
docpath = req.env.doc2path(req.env.docname)
217+
repo_path = path.relpath(docpath, self.repo.working_dir)
218+
paths = [repo_path]
219+
220+
git_revs = get_git_revisions(self.repo, req.env, paths)
244221

245222
if group_by:
246223
groups = OrderedDict()

tests/roots/test-recentupdate-path-filter/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ Recentupdate Path Filter Test
44
.. data.render::
55
:debug:
66
7-
{% for r in load_extra('recentupdate', count=10, path='subdir') %}
7+
{% for r in load_extra('recentupdate', count=10, paths=['subdir']) %}
88
Commit: {{ r.message[0] }}
99
{% endfor %}

0 commit comments

Comments
 (0)