← 返回首页
Address rev-parse review feedback · gitpython-developers/GitPython@6cf7ac3 · GitHub
Skip to content

Navigation Menu

Toggle navigation
Sign in
Appearance settings
Search or jump to...

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Resetting focus

Commit 6cf7ac3

Browse files
authored andcommitted
Address rev-parse review feedback
1 parent bdbdf4b commit 6cf7ac3

2 files changed

Lines changed: 73 additions & 18 deletions

File tree

‎git/repo/fun.py‎

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
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

4040
from 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+
502540
def _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

529567
def _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 == "":

‎test/test_rev_parse.py‎

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1+
# Copyright (C) 2026 Michael Trier (mtrier@gmail.com) and contributors
2+
#
3+
# This module is part of GitPython and is released under the
4+
# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/
5+
16
from pathlib import Path
27

38
import pytest
49

510
from git import Repo
11+
from git.refs import RemoteReference
12+
from git.refs import SymbolicReference
613
from gitdb.exc import BadName
714

815

@@ -31,14 +38,12 @@ def rev_parse_repo(tmp_path):
3138
repo.create_tag("v1.0", ref=release)
3239
main = repo.active_branch
3340

34-
side = repo.create_head("side", root)
35-
side.checkout()
3641
_write(repo, "side.txt", "side\n")
37-
side_commit = repo.index.commit("side branch")
42+
side_commit = repo.index.commit("side branch", parent_commits=[root], head=False, skip_hooks=True)
43+
repo.create_head("side", side_commit)
3844

39-
main.checkout()
40-
repo.git.merge("--no-ff", "side", "-m", "merge side")
41-
merge = repo.head.commit
45+
merge = repo.index.commit("merge side", parent_commits=[release, side_commit], skip_hooks=True)
46+
repo.head.log_append(side_commit.binsha, "checkout: moving from side to main", merge.binsha)
4247

4348
repo.create_head("aaaaaaaa", merge)
4449
repo.create_tag("@foo", ref=merge)
@@ -55,16 +60,21 @@ def rev_parse_repo(tmp_path):
5560

5661
def test_rev_parse_names_hex_and_describe_forms(rev_parse_repo):
5762
repo = rev_parse_repo["repo"]
63+
release = rev_parse_repo["release"]
5864
merge = rev_parse_repo["merge"]
5965

6066
assert repo.rev_parse("@") == merge
6167
assert repo.rev_parse("@foo") == merge
6268
assert repo.rev_parse("aaaaaaaa") == merge
6369
assert repo.rev_parse(merge.hexsha[:7]) == merge
70+
describe_name = "anything-9-g%s" % merge.hexsha[:7]
6471
assert repo.rev_parse("v1.0-1-g%s" % merge.hexsha[:7]) == merge
65-
assert repo.rev_parse("anything-9-g%s" % merge.hexsha[:7]) == merge
72+
assert repo.rev_parse(describe_name) == merge
6673
assert repo.rev_parse("%s-dirty" % merge.hexsha[:7]) == merge
6774

75+
repo.create_tag(describe_name, ref=release)
76+
assert repo.rev_parse(describe_name) == release
77+
6878

6979
def test_rev_parse_navigation_and_peeling(rev_parse_repo):
7080
repo = rev_parse_repo["repo"]
@@ -87,7 +97,8 @@ def test_rev_parse_navigation_and_peeling(rev_parse_repo):
8797
assert repo.rev_parse("ann^{}") == root
8898
assert repo.rev_parse("ann^{commit}") == root
8999
assert repo.rev_parse("HEAD^{tree}") == merge.tree
90-
assert repo.rev_parse("HEAD^{/}") == merge
100+
with pytest.raises(ValueError):
101+
repo.rev_parse("HEAD^{/}")
91102

92103

93104
def test_rev_parse_tree_and_index_paths(rev_parse_repo):
@@ -114,6 +125,10 @@ def test_rev_parse_reflog_selectors(rev_parse_repo):
114125
assert repo.rev_parse("%s@{0}" % main.name) == merge
115126
assert repo.rev_parse("@{-1}") == side
116127

128+
SymbolicReference.create(repo, "refs/remotes/origin/%s" % main.name, merge)
129+
main.set_tracking_branch(RemoteReference(repo, "refs/remotes/origin/%s" % main.name))
130+
assert repo.rev_parse("%s@{upstream}" % main.name) == merge
131+
117132

118133
def test_rev_parse_commit_message_search(rev_parse_repo):
119134
repo = rev_parse_repo["repo"]
@@ -132,6 +147,10 @@ def test_rev_parse_rejects_invalid_object_specs(rev_parse_repo):
132147
repo.rev_parse(":")
133148
with pytest.raises(ValueError):
134149
repo.rev_parse(":/")
150+
with pytest.raises(ValueError):
151+
repo.rev_parse(":/[")
152+
with pytest.raises(ValueError):
153+
repo.rev_parse("HEAD^{/[}")
135154
with pytest.raises(ValueError):
136155
repo.rev_parse("@{-0}")
137156
with pytest.raises(ValueError):

0 commit comments

Comments
 (0)

Footer

© 2026 GitHub, Inc.