Skip to content

Commit eeba9f4

Browse files
authored
Merge pull request #157 from timvink/113-ignore-commits
Add `ignored_commits_file` option
2 parents e777f78 + b6bd1bf commit eeba9f4

File tree

8 files changed

+147
-11
lines changed

8 files changed

+147
-11
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Thanks for considering to contribute to this project! Some guidelines:
1212
Make sure to install an editable version before running the tests:
1313

1414
```python
15-
pip install -r requirement_dev.txt
15+
pip install -r requirements_dev.txt
1616
pip install -e .
1717
pytest --cov=mkdocs_git_revision_date_localized_plugin --cov-report term-missing tests/
1818
```

docs/options.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ You can customize the plugin by setting options in `mkdocs.yml`. For example:
1818
enable_git_follow: true
1919
enabled: true
2020
strict: true
21+
ignored_commits_file: .git-blame-ignore-revs
2122
```
2223
2324
## `type`
@@ -155,3 +156,18 @@ Default is `true`. When enabled, the logs will show warnings when something is w
155156
- git-revision-date-localized:
156157
strict: true
157158
```
159+
160+
## `ignored_commits_file`
161+
162+
Default is `None`. You can specify a file path (relative to your `mkdocs.yml` directory) that contains a list of commit hashes to ignore
163+
when determining the revision date. The format of the file is the same as the format of
164+
git `blame.ignoreRevsFile`. This can be useful to ignore specific commits that apply formatting updates or other mass changes to the documents.
165+
166+
167+
=== ":octicons-file-code-16: mkdocs.yml"
168+
169+
```yaml
170+
plugins:
171+
- git-revision-date-localized:
172+
ignored_commits_file: .git-blame-ignore-revs
173+
```

src/mkdocs_git_revision_date_localized_plugin/plugin.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ class GitRevisionDateLocalizedPlugin(BasePlugin):
4343
("enable_creation_date", config_options.Type(bool, default=False)),
4444
("enabled", config_options.Type(bool, default=True)),
4545
("strict", config_options.Type(bool, default=True)),
46-
("enable_git_follow", config_options.Type(bool, default=True))
46+
("enable_git_follow", config_options.Type(bool, default=True)),
47+
("ignored_commits_file", config_options.Type(str, default=None)),
4748
)
4849

4950
def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]:
@@ -66,7 +67,7 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]:
6667

6768
assert self.config['type'] in ["date","datetime","iso_date","iso_datetime","timeago","custom"]
6869

69-
self.util = Util(config=self.config)
70+
self.util = Util(config=self.config, mkdocs_dir=os.path.abspath(os.path.dirname(config.get('config_file_path'))))
7071

7172
# Save last commit timestamp for entire site
7273
self.last_site_revision_timestamp = self.util.get_git_commit_timestamp(

src/mkdocs_git_revision_date_localized_plugin/util.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
NoSuchPathError,
1717
)
1818

19-
from typing import Dict
19+
from typing import Any, Dict, List
2020

2121
logger = logging.getLogger("mkdocs.plugins")
2222

@@ -27,11 +27,15 @@ class Util:
2727
This helps find git and calculate relevant dates.
2828
"""
2929

30-
def __init__(self, config={}):
30+
def __init__(self, config: Dict, mkdocs_dir: str):
3131
"""Initialize utility class."""
3232
self.config = config
3333
self.repo_cache = {}
3434

35+
ignore_commits_file = self.config.get("ignored_commits_file")
36+
ignore_commits_filepath = os.path.join(mkdocs_dir, ignore_commits_file) if ignore_commits_file else None
37+
self.ignored_commits = self.parse_git_ignore_revs(ignore_commits_filepath) if ignore_commits_file else []
38+
3539
def _get_repo(self, path: str) -> Git:
3640
if not os.path.isdir(path):
3741
path = os.path.dirname(path)
@@ -63,6 +67,7 @@ def get_git_commit_timestamp(
6367
int: commit date in unix timestamp, starts with the most recent commit.
6468
"""
6569
commit_timestamp = ""
70+
n_ignored_commits = 0
6671

6772
# Determine the logging level
6873
# Only log warnings when plugin is set to strict.
@@ -82,6 +87,7 @@ def get_git_commit_timestamp(
8287

8388
follow_option=self.config.get('enable_git_follow')
8489

90+
# Ignored commits are only considered for the most recent update, not for creation
8591
if is_first_commit:
8692
# diff_filter="A" will select the commit that created the file
8793
commit_timestamp = git.log(
@@ -100,6 +106,25 @@ def get_git_commit_timestamp(
100106
ignore_all_space=True, ignore_blank_lines=True
101107
)
102108

109+
# Retrieve the history for the file in the format <hash> <timestamp>
110+
# The maximum number of commits we will ever need to examine is 1 more than the number of ignored commits.
111+
lines = git.log(
112+
realpath, date="unix", format="%H %at", n=len(self.ignored_commits)+1, no_show_signature=True,
113+
).split("\n")
114+
115+
# process the commits for the file in reverse-chronological order. Ignore any commit that is on the
116+
# ignored list. If the line is empty, we've reached the end and need to use the fallback behavior.
117+
for line in lines:
118+
if not line:
119+
commit_timestamp = ""
120+
break
121+
commit, commit_timestamp = line.split(" ")
122+
if not any(commit.startswith(x) for x in self.ignored_commits):
123+
break
124+
else:
125+
n_ignored_commits += 1
126+
127+
103128
except (InvalidGitRepositoryError, NoSuchPathError) as err:
104129
if self.config.get('fallback_to_build_date'):
105130
log(
@@ -145,10 +170,13 @@ def get_git_commit_timestamp(
145170
# create timestamp
146171
if commit_timestamp == "":
147172
commit_timestamp = time.time()
148-
log(
173+
msg = (
149174
"[git-revision-date-localized-plugin] '%s' has no git logs, using current timestamp"
150175
% path
151176
)
177+
if n_ignored_commits:
178+
msg += f" (ignored {n_ignored_commits} commits)"
179+
log(msg)
152180

153181
return int(commit_timestamp)
154182

@@ -193,3 +221,26 @@ def add_spans(date_formats: Dict[str, str]) -> Dict[str, str]:
193221
% (date_type, datetime_string, date_string)
194222
)
195223
return date_formats
224+
225+
@staticmethod
226+
def parse_git_ignore_revs(filename: str) -> List[str]:
227+
"""
228+
Parses a file that is the same format as git's blame.ignoreRevsFile and return the list of commit hashes.
229+
230+
Whitespace, blanklines and comments starting with # are all ignored.
231+
"""
232+
result = []
233+
234+
try:
235+
with open(filename, "rt", encoding='utf-8') as f:
236+
for line in f:
237+
line = line.split("#", 1)[0].strip()
238+
if not line:
239+
continue
240+
result.append(line)
241+
except FileNotFoundError:
242+
logger.error(f"File not found: {filename}")
243+
except Exception as e:
244+
logger.error(f"An error occurred while reading the file {filename}: {e}")
245+
246+
return result
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
site/
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
site_name: test gitrevisiondatelocalized_plugin
2+
use_directory_urls: true
3+
4+
plugins:
5+
- search
6+
- git-revision-date-localized:
7+
enable_creation_date: True
8+
ignored_commits_file: ignored-commits.txt

tests/test_builds.py

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,15 @@ def setup_commit_history(testproject_path):
160160
repo.git.commit(message="add homepage", author=author, date="1500854705") # Mon Jul 24 2017 00:05:05 GMT+0000
161161

162162
file_name = os.path.join(testproject_path, "docs/page_with_tag.md")
163+
with open(file_name, "a") as the_file:
164+
the_file.write("test\n")
165+
repo.git.add("docs/page_with_tag.md")
166+
repo.git.commit(message="update homepage #1", author=author, date="1525475836") # Fri May 04 2018 23:17:16 GMT+0000
167+
163168
with open(file_name, "a") as the_file:
164169
the_file.write("awa\n")
165170
repo.git.add("docs/page_with_tag.md")
166-
repo.git.commit(message="update homepage", author=author, date="1642911026") # Sun Jan 23 2022 04:10:26 GMT+0000
171+
repo.git.commit(message="update homepage #2", author=author, date="1642911026") # Sun Jan 23 2022 04:10:26 GMT+0000
167172

168173
if os.path.exists("docs/page_with_renamed.md"):
169174
bf_file_name = os.path.join(testproject_path, "docs/page_with_renamed.md")
@@ -249,7 +254,7 @@ def validate_build(testproject_path, plugin_config: dict = {}):
249254
contents = page_with_tag.read_text(encoding="utf8")
250255
assert re.search(r"renders as\:\s[<span>|\w].+", contents)
251256

252-
repo = Util(config=plugin_config)
257+
repo = Util(config=plugin_config, mkdocs_dir=testproject_path)
253258
date_formats = repo.get_date_formats_for_timestamp(
254259
commit_timestamp=repo.get_git_commit_timestamp(
255260
path=str(testproject_path / "docs/page_with_tag.md"),
@@ -373,10 +378,10 @@ def test_tags_are_replaced(tmp_path, mkdocs_file):
373378
pytest.skip("Not necessary to test the JS library")
374379

375380
# Make sure count_commits() works
376-
# We created 10 commits in setup_commit_history()
381+
# We created 11 commits in setup_commit_history()
377382
with working_directory(testproject_path):
378-
u = Util()
379-
assert commit_count(u._get_repo("docs/page_with_tag.md")) == 10
383+
u = Util(config={}, mkdocs_dir=os.getcwd())
384+
assert commit_count(u._get_repo("docs/page_with_tag.md")) == 11
380385

381386

382387
# the revision date was in 'setup_commit_history' was set to 1642911026 (Sun Jan 23 2022 04:10:26 GMT+0000)
@@ -667,3 +672,37 @@ def test_mkdocs_genfiles_plugin(tmp_path):
667672
validate_build(
668673
testproject_path, plugin_config
669674
)
675+
676+
677+
def test_ignored_commits(tmp_path):
678+
testproject_path = setup_clean_mkdocs_folder(
679+
"tests/fixtures/basic_project/mkdocs_ignored_commits.yml", tmp_path
680+
)
681+
repo = setup_commit_history(testproject_path)
682+
683+
# First test that the middle commit doesn't show up by default
684+
# January 23, 2022 is the date of the most recent commit
685+
with open(str(testproject_path / "ignored-commits.txt"), "wt", encoding="utf-8") as fp:
686+
fp.write("")
687+
688+
result = build_docs_setup(testproject_path)
689+
assert result.exit_code == 0
690+
691+
page_with_tag = testproject_path / "site/page_with_tag/index.html"
692+
contents = page_with_tag.read_text(encoding="utf8")
693+
assert "January 23, 2022" in contents
694+
695+
# Now mark the most recent change to page_with_tag as ignored
696+
# May 4, 2018 is the date of the second most recent commit
697+
commit_hash = repo.git.log("docs/page_with_tag.md", format="%H", n=1)
698+
699+
with open(str(testproject_path / "ignored-commits.txt"), "wt", encoding="utf-8") as fp:
700+
fp.write(commit_hash)
701+
702+
# should not raise warning
703+
result = build_docs_setup(testproject_path)
704+
assert result.exit_code == 0
705+
706+
page_with_tag = testproject_path / "site/page_with_tag/index.html"
707+
contents = page_with_tag.read_text(encoding="utf8")
708+
assert "May 4, 2018" in contents

tests/test_parse_git_ignore_revs.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from mkdocs_git_revision_date_localized_plugin.util import Util
2+
import pytest
3+
import tempfile
4+
import os
5+
6+
TEST_PARAMS = [
7+
("abc123\n", ["abc123"]),
8+
("abc123 # comments are ignored\n", ["abc123"]),
9+
("\n\n\n\n\nabc123\n\n\n\n\n", ["abc123"]),
10+
]
11+
12+
@pytest.mark.parametrize("test_input,expected", TEST_PARAMS)
13+
def test_parse_git_ignore_revs(test_input, expected):
14+
with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', delete=False) as fp:
15+
fp.write(test_input)
16+
temp_file_name = fp.name
17+
try:
18+
assert Util.parse_git_ignore_revs(temp_file_name) == expected
19+
finally:
20+
os.remove(temp_file_name)

0 commit comments

Comments
 (0)