Skip to content
Merged
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
69 changes: 60 additions & 9 deletions src/mkdocs_git_revision_date_localized_plugin/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import re
import os
import time
import multiprocessing
from pathlib import Path

from mkdocs import __version__ as mkdocs_version
Expand Down Expand Up @@ -58,6 +57,29 @@ def __init__(self):
super().__init__()
self.last_revision_commits = {}
self.created_commits = {}
self.is_serve_dirty_build = False

def on_startup(self, *, command: str, dirty: bool) -> None:
"""
Run on startup.

Note that "The presence of an on_startup method (even if empty)
migrates the plugin to the new system where the plugin object is
kept across builds within one mkdocs serve."

Args:
command (str): The mkdocs command being run.
dirty (bool): Whether the build is dirty.
"""
# Track if this is an incremental rebuild during mkdocs serve
# dirty=True means it's a rebuild triggered by file changes
# dirty=False means it's a clean/initial build
self.is_serve_dirty_build = dirty

# Clear cache on clean builds to ensure fresh data
if not dirty:
self.last_revision_commits = {}
self.created_commits = {}

def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]:
"""
Expand Down Expand Up @@ -153,6 +175,7 @@ def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]:
return config

def parallel_compute_commit_timestamps(self, files, original_source: Optional[Dict] = None, is_first_commit=False):
import multiprocessing
pool = multiprocessing.Pool(processes=min(10, multiprocessing.cpu_count()))
results = []
for f in files:
Expand All @@ -165,15 +188,20 @@ def parallel_compute_commit_timestamps(self, files, original_source: Optional[Di
elif exclude(f.src_path, self.config.get("exclude", [])):
continue
else:
temp_abs_src_path = str(Path(f.abs_src_path).absolute())
abs_src_path = f.abs_src_path

# Support plugins like monorepo that might have moved the files from the original source that is under git
if original_source and abs_src_path in original_source:
abs_src_path = original_source[abs_src_path]

assert Path(abs_src_path).exists()
abs_src_path = str(Path(abs_src_path).absolute())
result = pool.apply_async(self.util.get_git_commit_timestamp, args=(abs_src_path, is_first_commit))
# Store both the original path and temp path (if different) so cache lookups work either way
results.append((abs_src_path, result))
if temp_abs_src_path != abs_src_path:
results.append((temp_abs_src_path, result))
pool.close()
pool.join()
if is_first_commit:
Expand All @@ -190,6 +218,13 @@ def on_files(self, files: Files, config: MkDocsConfig):
if not self.config.get("enabled") or not self.config.get("enable_parallel_processing"):
return

# Skip parallel processing on incremental rebuilds (dirty builds during mkdocs serve)
# This avoids the overhead of creating a new multiprocessing pool on every file save
# The cache from the initial build will be reused
if self.is_serve_dirty_build:
logging.debug("[git-revision-date-localized] Skipping parallel processing on incremental rebuild, using cache")
return

# Support monorepo/techdocs, which copies the docs_dir to a temporary directory
mono_repo_plugin = config.get("plugins", {}).get("monorepo", None)
if mono_repo_plugin is not None and hasattr(mono_repo_plugin, "merger") and mono_repo_plugin.merger is not None:
Expand Down Expand Up @@ -261,10 +296,18 @@ def on_page_markdown(self, markdown: str, page: Page, config: config_options.Con
if getattr(page.file, "generated_by", None):
last_revision_hash, last_revision_timestamp = "", int(time.time())
else:
last_revision_hash, last_revision_timestamp = self.last_revision_commits.get(
str(Path(page.file.abs_src_path).absolute()), (None, None)
)
if last_revision_timestamp is None:
# Use cached results if parallel processing is enabled and cache is populated
if self.config.get("enable_parallel_processing") and self.last_revision_commits:
last_revision_hash, last_revision_timestamp = self.last_revision_commits.get(
str(Path(page.file.abs_src_path).absolute()), (None, None)
)
if last_revision_timestamp is None:
last_revision_hash, last_revision_timestamp = self.util.get_git_commit_timestamp(
path=page.file.abs_src_path,
is_first_commit=False,
)
else:
# Directly call git if parallel processing is disabled or cache is empty
last_revision_hash, last_revision_timestamp = self.util.get_git_commit_timestamp(
path=page.file.abs_src_path,
is_first_commit=False,
Expand Down Expand Up @@ -337,10 +380,18 @@ def on_page_markdown(self, markdown: str, page: Page, config: config_options.Con
if getattr(page.file, "generated_by", None):
first_revision_hash, first_revision_timestamp = "", int(time.time())
else:
first_revision_hash, first_revision_timestamp = self.created_commits.get(
str(Path(page.file.abs_src_path).absolute()), (None, None)
)
if first_revision_timestamp is None:
# Use cached results if parallel processing is enabled and cache is populated
if self.config.get("enable_creation_date") and self.config.get("enable_parallel_processing") and self.created_commits:
first_revision_hash, first_revision_timestamp = self.created_commits.get(
str(Path(page.file.abs_src_path).absolute()), (None, None)
)
if first_revision_timestamp is None:
first_revision_hash, first_revision_timestamp = self.util.get_git_commit_timestamp(
path=page.file.abs_src_path,
is_first_commit=True,
)
else:
# Directly call git if parallel processing is disabled or cache is empty
first_revision_hash, first_revision_timestamp = self.util.get_git_commit_timestamp(
path=page.file.abs_src_path,
is_first_commit=True,
Expand Down