3535
3636# Typing ----------------------------------------------------------------------
3737
38- from typing import Optional , TYPE_CHECKING , Tuple , Union , cast , overload
38+ from typing import Iterator , Optional , TYPE_CHECKING , Tuple , Union , cast , overload
3939
4040from git .types import AnyGitObject , Literal , PathLike
4141
@@ -190,10 +190,6 @@ def name_to_object(repo: "Repo", name: str, return_ref: bool = False) -> Union[A
190190 # END handle short shas
191191 # END find sha if it matches
192192
193- if hexsha is None :
194- hexsha = _describe_to_long (repo , name )
195- # END handle describe output
196-
197193 # If we couldn't find an object for what seemed to be a short hexsha, try to find it
198194 # as reference anyway, it could be named 'aaa' for instance.
199195 if hexsha is None :
@@ -216,6 +212,10 @@ def name_to_object(repo: "Repo", name: str, return_ref: bool = False) -> Union[A
216212 # END for each base
217213 # END handle hexsha
218214
215+ if hexsha is None :
216+ hexsha = _describe_to_long (repo , name )
217+ # END handle describe output
218+
219219 # Didn't find any ref, this is an error.
220220 if return_ref :
221221 raise BadObject ("Couldn't find reference named %r" % name )
@@ -363,6 +363,8 @@ def _tracking_branch_object(repo: "Repo", ref: Optional[SymbolicReference]) -> A
363363 raise BadName ("@{upstream}" ) from e
364364 elif isinstance (ref , Head ):
365365 head = ref
366+ elif os .fspath (ref .path ).startswith ("refs/heads/" ):
367+ head = Head (repo , ref .path )
366368 else :
367369 raise BadName ("%s@{upstream}" % ref .name )
368370 # END handle head
@@ -479,11 +481,15 @@ def _find_commit_by_message(
479481 repo : "Repo" , rev : Optional [AnyGitObject ], pattern : str , braced : bool = False
480482) -> AnyGitObject :
481483 pattern , negated = _parse_search (_unescape_braced_regex (pattern ) if braced else pattern )
482- regex = re .compile (pattern )
484+ try :
485+ regex = re .compile (pattern )
486+ except re .error as e :
487+ raise ValueError ("Invalid commit message regex %r" % pattern ) from e
488+ # END handle invalid regex
483489 if rev is None :
484- commits = repo . iter_commits ( "--all" )
490+ commits = _all_ref_commits ( repo )
485491 else :
486- commits = repo . iter_commits ( to_commit (cast (Object , rev )). hexsha )
492+ commits = _reachable_commits ([ to_commit (cast (Object , rev ))] )
487493 # END handle starting point
488494
489495 for commit in commits :
@@ -499,6 +505,38 @@ def _find_commit_by_message(
499505 raise BadName ("No commit found matching message pattern %r" % pattern )
500506
501507
508+ def _all_ref_commits (repo : "Repo" ) -> Iterator ["Commit" ]:
509+ starts = []
510+ for ref in repo .references :
511+ try :
512+ starts .append (to_commit (cast (Object , ref .object )))
513+ except (BadName , ValueError ):
514+ pass
515+ # END skip refs that do not point to commits
516+ # END for each ref
517+ try :
518+ starts .append (repo .head .commit )
519+ except ValueError :
520+ pass
521+ # END handle unborn head
522+ return _reachable_commits (starts )
523+
524+
525+ def _reachable_commits (starts : list ["Commit" ]) -> Iterator ["Commit" ]:
526+ seen = set ()
527+ pending = starts [:]
528+ while pending :
529+ pending .sort (key = lambda commit : commit .committed_date , reverse = True )
530+ commit = pending .pop (0 )
531+ if commit .binsha in seen :
532+ continue
533+ # END skip seen commit
534+ seen .add (commit .binsha )
535+ yield commit
536+ pending .extend (commit .parents )
537+ # END while commits remain
538+
539+
502540def _index_lookup (repo : "Repo" , spec : str ) -> AnyGitObject :
503541 if not spec :
504542 raise ValueError ("':' must be followed by a path" )
@@ -527,8 +565,6 @@ def _tree_lookup(obj: AnyGitObject, path: str) -> AnyGitObject:
527565
528566
529567def _peel (obj : AnyGitObject , output_type : str , repo : "Repo" , rev : str ) -> AnyGitObject :
530- if output_type == "/" :
531- return obj
532568 if output_type .startswith ("/" ):
533569 return _find_commit_by_message (repo , obj , output_type [1 :], braced = True )
534570 if output_type == "" :
0 commit comments