-
-
Notifications
You must be signed in to change notification settings - Fork 281
refactor(git): code cleanup and test coverage #1417
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
1cc5858
61c254f
40abbbd
20f4892
3962b80
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -1,34 +1,14 @@ | ||||||||
from __future__ import annotations | ||||||||
|
||||||||
import os | ||||||||
from enum import Enum | ||||||||
from os import linesep | ||||||||
from pathlib import Path | ||||||||
from tempfile import NamedTemporaryFile | ||||||||
|
||||||||
from commitizen import cmd, out | ||||||||
from commitizen.exceptions import GitCommandError | ||||||||
|
||||||||
UNIX_EOL = "\n" | ||||||||
WINDOWS_EOL = "\r\n" | ||||||||
|
||||||||
|
||||||||
class EOLTypes(Enum): | ||||||||
"""The EOL type from `git config core.eol`.""" | ||||||||
|
||||||||
LF = "lf" | ||||||||
CRLF = "crlf" | ||||||||
NATIVE = "native" | ||||||||
|
||||||||
def get_eol_for_open(self) -> str: | ||||||||
"""Get the EOL character for `open()`.""" | ||||||||
map = { | ||||||||
EOLTypes.CRLF: WINDOWS_EOL, | ||||||||
EOLTypes.LF: UNIX_EOL, | ||||||||
EOLTypes.NATIVE: linesep, | ||||||||
} | ||||||||
|
||||||||
return map[self] | ||||||||
_UNIX_EOL = "\n" | ||||||||
_WINDOWS_EOL = "\r\n" | ||||||||
|
||||||||
|
||||||||
class GitObject: | ||||||||
|
@@ -37,9 +17,7 @@ class GitObject: | |||||||
date: str | ||||||||
|
||||||||
def __eq__(self, other) -> bool: | ||||||||
if not hasattr(other, "rev"): | ||||||||
return False | ||||||||
return self.rev == other.rev # type: ignore | ||||||||
return hasattr(other, "rev") and self.rev == other.rev | ||||||||
|
||||||||
|
||||||||
class GitCommit(GitObject): | ||||||||
|
@@ -63,6 +41,20 @@ def __init__( | |||||||
def message(self): | ||||||||
return f"{self.title}\n\n{self.body}".strip() | ||||||||
|
||||||||
@classmethod | ||||||||
def from_rev_and_commit(cls, rev_and_commit: str) -> GitCommit: | ||||||||
rev, parents, title, author, author_email, *body_list = rev_and_commit.split( | ||||||||
"\n" | ||||||||
) | ||||||||
return cls( | ||||||||
rev=rev.strip(), | ||||||||
title=title.strip(), | ||||||||
body="\n".join(body_list).strip(), | ||||||||
author=author, | ||||||||
author_email=author_email, | ||||||||
parents=[p for p in parents.strip().split(" ") if p], | ||||||||
) | ||||||||
|
||||||||
def __repr__(self): | ||||||||
return f"{self.title} ({self.rev})" | ||||||||
|
||||||||
|
@@ -101,13 +93,11 @@ def tag( | |||||||
# according to https://git-scm.com/book/en/v2/Git-Basics-Tagging, | ||||||||
# we're not able to create lightweight tag with message. | ||||||||
# by adding message, we make it a annotated tags | ||||||||
c = cmd.run(f'git tag {_opt} "{tag if _opt == "" or msg is None else msg}"') | ||||||||
return c | ||||||||
return cmd.run(f'git tag {_opt} "{tag if _opt == "" or msg is None else msg}"') | ||||||||
|
||||||||
|
||||||||
def add(*args: str) -> cmd.Command: | ||||||||
c = cmd.run(f"git add {' '.join(args)}") | ||||||||
return c | ||||||||
return cmd.run(f"git add {' '.join(args)}") | ||||||||
|
||||||||
|
||||||||
def commit( | ||||||||
|
@@ -140,24 +130,10 @@ def get_commits( | |||||||
) -> list[GitCommit]: | ||||||||
"""Get the commits between start and end.""" | ||||||||
git_log_entries = _get_log_as_str_list(start, end, args) | ||||||||
git_commits = [] | ||||||||
for rev_and_commit in git_log_entries: | ||||||||
if not rev_and_commit: | ||||||||
continue | ||||||||
rev, parents, title, author, author_email, *body_list = rev_and_commit.split( | ||||||||
"\n" | ||||||||
) | ||||||||
if rev_and_commit: | ||||||||
git_commit = GitCommit( | ||||||||
rev=rev.strip(), | ||||||||
title=title.strip(), | ||||||||
body="\n".join(body_list).strip(), | ||||||||
author=author, | ||||||||
author_email=author_email, | ||||||||
parents=[p for p in parents.strip().split(" ") if p], | ||||||||
) | ||||||||
git_commits.append(git_commit) | ||||||||
return git_commits | ||||||||
return [ | ||||||||
GitCommit.from_rev_and_commit(rev_and_commit) | ||||||||
for rev_and_commit in filter(None, git_log_entries) | ||||||||
] | ||||||||
|
||||||||
|
||||||||
def get_filenames_in_commit(git_reference: str = ""): | ||||||||
|
@@ -170,8 +146,7 @@ def get_filenames_in_commit(git_reference: str = ""): | |||||||
c = cmd.run(f"git show --name-only --pretty=format: {git_reference}") | ||||||||
if c.return_code == 0: | ||||||||
return c.out.strip().split("\n") | ||||||||
else: | ||||||||
raise GitCommandError(c.err) | ||||||||
raise GitCommandError(c.err) | ||||||||
|
||||||||
|
||||||||
def get_tags( | ||||||||
|
@@ -197,16 +172,11 @@ def get_tags( | |||||||
if c.err: | ||||||||
out.warn(f"Attempting to proceed after: {c.err}") | ||||||||
|
||||||||
if not c.out: | ||||||||
return [] | ||||||||
|
||||||||
git_tags = [ | ||||||||
return [ | ||||||||
GitTag.from_line(line=line, inner_delimiter=inner_delimiter) | ||||||||
for line in c.out.split("\n")[:-1] | ||||||||
] | ||||||||
|
||||||||
return git_tags | ||||||||
|
||||||||
|
||||||||
def tag_exist(tag: str) -> bool: | ||||||||
c = cmd.run(f"git tag --list {tag}") | ||||||||
|
@@ -231,18 +201,18 @@ def get_tag_message(tag: str) -> str | None: | |||||||
return c.out.strip() | ||||||||
|
||||||||
|
||||||||
def get_tag_names() -> list[str | None]: | ||||||||
def get_tag_names() -> list[str]: | ||||||||
c = cmd.run("git tag --list") | ||||||||
if c.err: | ||||||||
return [] | ||||||||
return [tag.strip() for tag in c.out.split("\n") if tag.strip()] | ||||||||
return list(filter(None, (tag.strip() for tag in c.out.split("\n")))) | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isn't this less performant and harder to read? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought evaluating There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both of these expressions are meant to process a string
They produce the same final list, but they differ in style, performance, and readability. 🔹 Version 1: List comprehension with
|
Use case | Recommended expression |
---|---|
Clarity and simplicity | [tag.strip() for tag in ... if tag.strip()] |
Performance or one-liner style | list(filter(None, (tag.strip() for tag in ...))) |
If you're stripping expensive strings or the list is very large, prefer the second one.
You can also avoid double strip()
in version 1 by assigning to a temporary:
[tag for raw in c.out.split("\n") if (tag := raw.strip())]
(Requires Python 3.8+ — uses the walrus operator :=
.)
Let me know if you want a performance comparison or an explanation of generator expressions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now I use [tag for raw in c.out.split("\n") if (tag := raw.strip())]
here, thanks.
bearomorphism marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why remove the enum?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just found it hard to trace because it first converts a string to a enum and then converts it back to a string. I thought skipping the enum part may make more sense.
I added the enum back but reordered and rename the functions. Please review
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wouldn't elif
convey the meaning better? it's either this or that. With if -> if
someone could introduce a refactor allowing you to fall into the next if
which should not happen
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm
I prefer not to include an else
clause after an if
block that concludes with a return
, as it is typically unnecessary, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
either way works for me 🤔
Uh oh!
There was an error while loading. Please reload this page.