55from functools import lru_cache
66from pathlib import Path
77from tempfile import NamedTemporaryFile
8+ from typing import TYPE_CHECKING
89
910from commitizen import cmd , out
1011from commitizen .exceptions import GitCommandError
1112
13+ if TYPE_CHECKING :
14+ from collections .abc import Sequence
15+
1216
1317class EOLType (Enum ):
1418 """The EOL type from `git config core.eol`."""
@@ -19,7 +23,7 @@ class EOLType(Enum):
1923
2024 @classmethod
2125 def for_open (cls ) -> str :
22- c = cmd .run ("git config core.eol" )
26+ c = cmd .run ([ "git" , " config" , " core.eol"] )
2327 eol = c .out .strip ().upper ()
2428 return cls ._char_for_open ()[cls ._safe_cast (eol )]
2529
@@ -164,49 +168,47 @@ def tag(
164168 tag : str , annotated : bool = False , signed : bool = False , msg : str | None = None
165169) -> cmd .Command :
166170 if not annotated and not signed :
167- return cmd .run (f "git tag { tag } " )
171+ return cmd .run ([ "git" , " tag" , tag ] )
168172
169173 # according to https://git-scm.com/book/en/v2/Git-Basics-Tagging,
170174 # we're not able to create lightweight tag with message.
171175 # by adding message, we make it a annotated tags
172176 option = "-s" if signed else "-a" # The else case is for annotated tags
173- return cmd .run (f' git tag { option } { tag } -m " { msg or tag } "' )
177+ return cmd .run ([ " git" , " tag" , option , tag , "-m" , msg or tag ] )
174178
175179
176180def add (* args : str ) -> cmd .Command :
177- return cmd .run (f "git add { ' ' . join ( args ) } " )
181+ return cmd .run ([ "git" , " add" , * args ] )
178182
179183
180184def commit (
181185 message : str ,
182- args : str = "" ,
186+ args : Sequence [ str ] = () ,
183187 committer_date : str | None = None ,
184188) -> cmd .Command :
185189 f = NamedTemporaryFile ("wb" , delete = False )
186190 f .write (message .encode ("utf-8" ))
187191 f .close ()
188192
189- command = _create_commit_cmd_string ( args , committer_date , f . name )
190- c = cmd . run ( command )
191- os . unlink ( f . name )
192- return c
193+ cmd_args = [ "git" , "commit" ]
194+ if args :
195+ cmd_args . extend ( args )
196+ cmd_args . extend ([ "-F" , f . name ])
193197
198+ env : dict [str , str ] | None = None
199+ if committer_date :
200+ env = {"GIT_COMMITTER_DATE" : committer_date }
194201
195- def _create_commit_cmd_string (args : str , committer_date : str | None , name : str ) -> str :
196- command = f'git commit { args } -F "{ name } "'
197- if not committer_date :
198- return command
199- if os .name != "nt" :
200- return f"GIT_COMMITTER_DATE={ committer_date } { command } "
201- # Using `cmd /v /c "{command}"` sets environment variables only for that command
202- return f'cmd /v /c "set GIT_COMMITTER_DATE={ committer_date } && { command } "'
202+ c = cmd .run (cmd_args , env = env )
203+ os .unlink (f .name )
204+ return c
203205
204206
205207def get_commits (
206208 start : str | None = None ,
207209 end : str | None = None ,
208210 * ,
209- args : str = "" ,
211+ args : Sequence [ str ] = () ,
210212) -> list [GitCommit ]:
211213 """Get the commits between start and end."""
212214 if end is None :
@@ -226,7 +228,10 @@ def get_filenames_in_commit(git_reference: str = "") -> list[str]:
226228
227229 :returns: file names committed in the last commit by default or inside the passed git reference
228230 """
229- c = cmd .run (f"git show --name-only --pretty=format: { git_reference } " )
231+ cmd_args = ["git" , "show" , "--name-only" , "--pretty=format:" ]
232+ if git_reference :
233+ cmd_args .append (git_reference )
234+ c = cmd .run (cmd_args )
230235 if c .return_code == 0 :
231236 return c .out .strip ().split ("\n " )
232237 raise GitCommandError (c .err )
@@ -237,15 +242,17 @@ def get_tags(
237242) -> list [GitTag ]:
238243 inner_delimiter = "---inner_delimiter---"
239244 formatter = (
240- f' "%(refname:strip=2){ inner_delimiter } '
245+ f"%(refname:strip=2){ inner_delimiter } "
241246 f"%(objectname){ inner_delimiter } "
242247 f"%(creatordate:format:{ dateformat } ){ inner_delimiter } "
243- f' %(object)"'
248+ f" %(object)"
244249 )
245- extra = "--merged" if reachable_only else ""
250+ cmd_args = ["git" , "tag" , f"--format={ formatter } " , "--sort=-creatordate" ]
251+ if reachable_only :
252+ cmd_args .append ("--merged" )
246253 # Force the default language for parsing
247254 env = {"LC_ALL" : "C" , "LANG" : "C" , "LANGUAGE" : "C" }
248- c = cmd .run (f"git tag --format= { formatter } --sort=-creatordate { extra } " , env = env )
255+ c = cmd .run (cmd_args , env = env )
249256 if c .return_code != 0 :
250257 if reachable_only and c .err == "fatal: malformed object name HEAD\n " :
251258 # this can happen if there are no commits in the repo yet
@@ -262,55 +269,55 @@ def get_tags(
262269
263270
264271def tag_exist (tag : str ) -> bool :
265- c = cmd .run (f "git tag --list { tag } " )
272+ c = cmd .run ([ "git" , " tag" , " --list" , tag ] )
266273 return tag in c .out
267274
268275
269276def is_signed_tag (tag : str ) -> bool :
270- return cmd .run (f "git tag -v { tag } " ).return_code == 0
277+ return cmd .run ([ "git" , " tag" , "-v" , tag ] ).return_code == 0
271278
272279
273280def get_latest_tag_name () -> str | None :
274- c = cmd .run ("git describe --abbrev=0 --tags" )
281+ c = cmd .run ([ "git" , " describe" , " --abbrev=0" , " --tags"] )
275282 if c .err :
276283 return None
277284 return c .out .strip ()
278285
279286
280287def get_tag_message (tag : str ) -> str | None :
281- c = cmd .run (f "git tag -l --format=' %(contents:subject)' { tag } " )
288+ c = cmd .run ([ "git" , " tag" , "-l" , " --format=%(contents:subject)" , tag ] )
282289 if c .err :
283290 return None
284291 return c .out .strip ()
285292
286293
287294def get_tag_names () -> list [str ]:
288- c = cmd .run ("git tag --list" )
295+ c = cmd .run ([ "git" , " tag" , " --list"] )
289296 if c .err :
290297 return []
291298 return [tag for raw in c .out .split ("\n " ) if (tag := raw .strip ())]
292299
293300
294301def find_git_project_root () -> Path | None :
295- c = cmd .run ("git rev-parse --show-toplevel" )
302+ c = cmd .run ([ "git" , " rev-parse" , " --show-toplevel"] )
296303 if c .err :
297304 return None
298305 return Path (c .out .strip ())
299306
300307
301308def is_staging_clean () -> bool :
302309 """Check if staging is clean."""
303- c = cmd .run ("git diff --no-ext-diff --cached --name-only" )
310+ c = cmd .run ([ "git" , " diff" , " --no-ext-diff" , " --cached" , " --name-only"] )
304311 return not bool (c .out )
305312
306313
307314def is_git_project () -> bool :
308- c = cmd .run ("git rev-parse --is-inside-work-tree" )
315+ c = cmd .run ([ "git" , " rev-parse" , " --is-inside-work-tree"] )
309316 return c .out .strip () == "true"
310317
311318
312319def get_core_editor () -> str | None :
313- c = cmd .run ("git var GIT_EDITOR" )
320+ c = cmd .run ([ "git" , " var" , " GIT_EDITOR"] )
314321 if c .out :
315322 return c .out .strip ()
316323 return None
@@ -321,21 +328,31 @@ def smart_open(*args, **kwargs): # type: ignore[no-untyped-def,unused-ignore] #
321328 return open (* args , newline = EOLType .for_open (), ** kwargs )
322329
323330
324- def _get_log_as_str_list (start : str | None , end : str , args : str ) -> list [str ]:
331+ def _get_log_as_str_list (start : str | None , end : str , args : Sequence [ str ] ) -> list [str ]:
325332 """Get string representation of each log entry"""
326333 delimiter = "----------commit-delimiter----------"
327334 log_format : str = "%H%n%P%n%s%n%an%n%ae%n%b"
328335 command_range = f"{ start } ..{ end } " if start else end
329- command = f"git -c log.showSignature=False log --pretty={ log_format } { delimiter } { args } { command_range } "
336+ cmd_args = [
337+ "git" ,
338+ "-c" ,
339+ "log.showSignature=False" ,
340+ "log" ,
341+ f"--pretty={ log_format } { delimiter } " ,
342+ ]
343+ if args :
344+ cmd_args .extend (args )
345+ if command_range :
346+ cmd_args .append (command_range )
330347
331- c = cmd .run (command )
348+ c = cmd .run (cmd_args )
332349 if c .return_code != 0 :
333350 raise GitCommandError (c .err )
334351 return c .out .split (f"{ delimiter } \n " )
335352
336353
337354def get_default_branch () -> str :
338- c = cmd .run ("git symbolic-ref refs/remotes/origin/HEAD" )
355+ c = cmd .run ([ "git" , " symbolic-ref" , " refs/remotes/origin/HEAD"] )
339356 if c .return_code != 0 :
340357 raise GitCommandError (c .err )
341358 return c .out .strip ()
0 commit comments