docs(git): Show source of renamed files

Description

Abstract

When a file is renamed, show the last source on the pages of the old and new paths, by following the modifications tree to look for the last modification with a source code.

Motivation

When we renamed the core domain context to code_repository, the source code of all included files disapeared from the “git” part of the documentation.

Rationale

A new function find_last_source_code is added in git_to_sphinx.py that will be executed recursively if the source code of file is not found, by checking in the parent modification if any, or the one of the old path in case of rename.

Info

Hash

5639022757fc3eceb42ba08a24c06bc8fa6e30ec

Date

2020-09-25 23:37:36 +0200

Parents
  • style: Prefix `id_is_positive_integer` with `validate_` [a307cce6]2020-09-25 23:37:35 +0200

Children
  • docs(git): Only update remote branches if asked [207325fe]2020-09-25 23:37:37 +0200

Branches
Tags

(No tags)

Changes

docs/git_to_sphinx.py

Type

Modified

Stats

+50 -19

@@ -13,6 +13,7 @@ import rstcheck
 from cached_property import cached_property
 from git.objects.util import from_timestamp
 from pydriller import RepositoryMining as RepositoryMiningBase
+from pydriller.domain.commit import ModificationType
 from pydriller.git_repository import GitRepository as GitRepositoryBase


@@ -363,9 +364,9 @@ Last update
 {new_path}
 {old_path}

-------
-Source
-------
+-----------
+Last source
+-----------

 {source_code}

@@ -717,25 +718,44 @@ def render_commit_pages(commit, children, branches, tags):
     }


-def render_file_pages(path, commits_and_modifications, current_tree):
-    commits_and_modifications = [
-        (commit, modification)
-        for commit, modification in commits_and_modifications
-        if not commit.merge
-    ]
+def find_last_source_code(path, change, changes_by_file):
+    commit, modification, parent_change = change
+    if modification.source_code or modification.added or modification.removed:
+        return modification.source_code
+    if parent_change:
+        return find_last_source_code(path, parent_change, changes_by_file)
+    if (
+        modification.change_type == ModificationType.RENAME
+        and modification.old_path
+        and modification.old_path != modification.new_path
+    ):
+        return find_last_source_code(
+            modification.old_path,
+            changes_by_file[modification.old_path][0],
+            changes_by_file,
+        )
+    return None
+
+
+def render_file_pages(path, changes_by_file, current_tree):
+    last_change = changes_by_file[path][0]
+    changes = [change for change in changes_by_file[path] if not change[0].merge]
     modifications = "\n\n".join(
         "\n"
         + render_modification_for_commit_in_path(modification, commit, path).strip()
-        for commit, modification in commits_and_modifications
+        for commit, modification, *__ in changes
     )
-    last_commit, last_modification = commits_and_modifications[0]
-    first_commit, first_modification = commits_and_modifications[-1]
+    last_commit, last_modification = changes[0][:2]
+    first_commit, first_modification = changes[-1][:2]
     basename = os.path.basename(path)
     dirname = os.path.dirname(path)
     suffixed_dirname = dirname + "/"
     title = basename
     if not path_in_tree(path, current_tree):
         title += REMOVED
+    source_code = last_change[1].source_code or find_last_source_code(
+        path, last_change, changes_by_file
+    )
     return {
         f"content/{escape_path(path)}.rst": FILE_PAGE_TEMPLATE.format(
             path=path,
@@ -745,9 +765,9 @@ def render_file_pages(path, commits_and_modifications, current_tree):
             escaped_dirname=escape_path(suffixed_dirname),
             modifications=modifications,
             source_code=SOURCE_CODE_TEMPLATE.format(
-                source_code=indent(last_modification.source_code, "   ")
+                source_code=indent(source_code, "   ")
             )
-            if last_modification.source_code
+            if source_code
             else "(No source code)",
             last_type=MODIFICATION_TYPES[last_modification.change_type.name],
             last_updated_at=format_date(last_commit.committer_date),
@@ -1003,7 +1023,7 @@ def render(location, basepath=BASEPATH, clean=True):
     print("")

     print("Rendering commits...", end="")
-    commits_by_file = defaultdict(list)
+    changes_by_file = defaultdict(list)
     nb_commits = len(commits)
     for index, (__, commit) in enumerate(commits.items(), 1):
         print(f"\rRendering commits [{index}/{nb_commits}]", end="")
@@ -1021,9 +1041,19 @@ def render(location, basepath=BASEPATH, clean=True):
         )
         for modification in commit.modifications:
             if modification.new_path:
-                commits_by_file[modification.new_path].append((commit, modification))
+                changes_by_file[modification.new_path].append(
+                    [commit, modification, None]
+                )
             if modification.old_path and modification.old_path != modification.new_path:
-                commits_by_file[modification.old_path].append((commit, modification))
+                changes_by_file[modification.old_path].append(
+                    [commit, modification, None]
+                )
+
+    # link changes to their parent
+    for path, changes in changes_by_file.items():
+        for index, change in enumerate(changes[:-1]):
+            change[2] = changes[index + 1]
+
     print("")

     print("Building content tree...", end="")
@@ -1031,9 +1061,9 @@ def render(location, basepath=BASEPATH, clean=True):
     print("\rBuilding content tree [ok]")

     print("Rendering files...", end="")
-    nb_files = len(commits_by_file)
+    nb_files = len(changes_by_file)
     dirs = defaultdict(set)
-    for index, path in enumerate(sorted(commits_by_file), 1):
+    for index, path in enumerate(sorted(changes_by_file), 1):
         print(f"\rRendering files [{index}/{nb_files}]", end="")

         current, is_dir = path, False
@@ -1047,8 +1077,7 @@ def render(location, basepath=BASEPATH, clean=True):
                 break

         render_to_files(
-            render_file_pages(path, commits_by_file[path], current_tree),
-            basepath=basepath,
+            render_file_pages(path, changes_by_file, current_tree), basepath=basepath
         )
     print("")