|
|
@@ -64,6 +64,7 @@ Currently implemented:
|
|
|
* update_server_info
|
|
|
* write_commit_graph
|
|
|
* status
|
|
|
+ * shortlog
|
|
|
* symbolic_ref
|
|
|
* worktree{_add,_list,_remove,_prune,_lock,_unlock,_move}
|
|
|
|
|
|
@@ -2670,6 +2671,48 @@ def status(
|
|
|
return GitStatus(tracked_changes, unstaged_changes, untracked_changes)
|
|
|
|
|
|
|
|
|
+def shortlog(
|
|
|
+ repo: Union[str, os.PathLike, Repo],
|
|
|
+ summary_only: bool = False,
|
|
|
+ sort_by_commits: bool = False,
|
|
|
+) -> list[dict[str, str]]:
|
|
|
+ """Summarize commits by author, like git shortlog.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ repo: Path to repository or Repo object.
|
|
|
+ summary_only: If True, only show counts per author.
|
|
|
+ sort_by_commits: If True, sort authors by number of commits.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ A list of dictionaries, each containing:
|
|
|
+ - "author": the author's name as a string
|
|
|
+ - "messages": all commit messages concatenated into a single string
|
|
|
+ """
|
|
|
+ with open_repo_closing(repo) as r:
|
|
|
+ walker = r.get_walker()
|
|
|
+ authors: dict[str, list[str]] = {}
|
|
|
+
|
|
|
+ for entry in walker:
|
|
|
+ commit = entry.commit
|
|
|
+
|
|
|
+ author = commit.author.decode(commit.encoding or "utf-8")
|
|
|
+ message = commit.message.decode(commit.encoding or "utf-8").strip()
|
|
|
+
|
|
|
+ authors.setdefault(author, []).append(message)
|
|
|
+
|
|
|
+ # Convert messages to single string per author
|
|
|
+ items: list[dict[str, str]] = [
|
|
|
+ {"author": author, "messages": "\n".join(msgs)}
|
|
|
+ for author, msgs in authors.items()
|
|
|
+ ]
|
|
|
+
|
|
|
+ if sort_by_commits:
|
|
|
+ # Sort by number of commits (lines in messages)
|
|
|
+ items.sort(key=lambda x: len(x["messages"].splitlines()), reverse=True)
|
|
|
+
|
|
|
+ return items
|
|
|
+
|
|
|
+
|
|
|
def _walk_working_dir_paths(
|
|
|
frompath: Union[str, bytes, os.PathLike],
|
|
|
basepath: Union[str, bytes, os.PathLike],
|