Skip to content

Commit 8899d19

Browse files
authored
Merge pull request #2343 from anticipate218/fix/preserve-blame-timezones
fix(repo): preserve timezone metadata in blame results
2 parents 23f33ef + 9aa6215 commit 8899d19

3 files changed

Lines changed: 41 additions & 2 deletions

File tree

‎git/repo/base.py‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
)
3636
from git.index import IndexFile
3737
from git.objects import Submodule, RootModule, Commit
38+
from git.objects.util import utctz_to_altz
3839
from git.refs import HEAD, Head, Reference, SymbolicReference, TagReference
3940
from git.remote import Remote, _T_RemoteName, add_progress, to_progress_instance
4041
from git.util import (
@@ -1363,11 +1364,13 @@ def blame_incremental(
13631364
safe_decode(props[b"author-mail"].lstrip(b"<").rstrip(b">")),
13641365
),
13651366
authored_date=int(props[b"author-time"]),
1367+
author_tz_offset=utctz_to_altz(props[b"author-tz"].decode("ascii")),
13661368
committer=Actor(
13671369
safe_decode(props[b"committer"]),
13681370
safe_decode(props[b"committer-mail"].lstrip(b"<").rstrip(b">")),
13691371
),
13701372
committed_date=int(props[b"committer-time"]),
1373+
committer_tz_offset=utctz_to_altz(props[b"committer-tz"].decode("ascii")),
13711374
)
13721375
commits[hexsha] = c
13731376
else:
@@ -1452,9 +1455,11 @@ class InfoTD(TypedDict, total=False):
14521455
author: str
14531456
author_email: str
14541457
author_date: int
1458+
author_tz_offset: int
14551459
committer: str
14561460
committer_email: str
14571461
committer_date: int
1462+
committer_tz_offset: int
14581463

14591464
info: InfoTD = {}
14601465

@@ -1504,20 +1509,24 @@ class InfoTD(TypedDict, total=False):
15041509
# committer Tom Preston-Werner
15051510
# committer-mail <tom@mojombo.com>
15061511
# committer-time 1192271832
1507-
# committer-tz -0700 - IGNORED BY US
1512+
# committer-tz -0700
15081513
role = m.group(0)
15091514
if role == "author":
15101515
if firstpart.endswith("-mail"):
15111516
info["author_email"] = parts[-1]
15121517
elif firstpart.endswith("-time"):
15131518
info["author_date"] = int(parts[-1])
1519+
elif firstpart.endswith("-tz"):
1520+
info["author_tz_offset"] = utctz_to_altz(parts[-1])
15141521
elif role == firstpart:
15151522
info["author"] = parts[-1]
15161523
elif role == "committer":
15171524
if firstpart.endswith("-mail"):
15181525
info["committer_email"] = parts[-1]
15191526
elif firstpart.endswith("-time"):
15201527
info["committer_date"] = int(parts[-1])
1528+
elif firstpart.endswith("-tz"):
1529+
info["committer_tz_offset"] = utctz_to_altz(parts[-1])
15211530
elif role == firstpart:
15221531
info["committer"] = parts[-1]
15231532
# END distinguish mail,time,name
@@ -1540,8 +1549,10 @@ class InfoTD(TypedDict, total=False):
15401549
hex_to_bin(sha),
15411550
author=Actor._from_string(f"{info['author']} {info['author_email']}"),
15421551
authored_date=info["author_date"],
1552+
author_tz_offset=info.get("author_tz_offset"),
15431553
committer=Actor._from_string(f"{info['committer']} {info['committer_email']}"),
15441554
committed_date=info["committer_date"],
1555+
committer_tz_offset=info.get("committer_tz_offset"),
15451556
)
15461557
commits[sha] = c
15471558
blames[-1][0] = c

‎test/test_repo.py‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,30 @@ def test_blame_preserves_line_content(self):
773773
lines = [line for _commit, blamed in repo.blame("HEAD", "file.txt") for line in blamed]
774774
self.assertEqual(lines, ["alpha ", "beta", "\t", "crlf\r", "gamma"])
775775

776+
def test_blame_preserves_timezones(self):
777+
with fixture_repo("blame-content.sh") as repo:
778+
for incremental in (False, True):
779+
for rev in ("HEAD", None):
780+
with self.subTest(incremental=incremental, rev=rev):
781+
entry = next(iter(repo.blame(rev, "file.txt", incremental=incremental)))
782+
commit = entry[0]
783+
porcelain = repo.git.blame(rev, "--line-porcelain", "--", "file.txt")
784+
offsets = dict(
785+
line.split(" ", 1)
786+
for line in porcelain.splitlines()
787+
if line.startswith(("author-tz ", "committer-tz "))
788+
)
789+
with mock.patch.object(
790+
Commit, "_set_cache_", side_effect=AssertionError("unexpected object read")
791+
):
792+
if rev is None:
793+
self.assertEqual(commit.hexsha, "0" * 40)
794+
else:
795+
self.assertEqual(commit.author_tz_offset, -19800)
796+
self.assertEqual(commit.committer_tz_offset, 14400)
797+
self.assertEqual(commit.authored_datetime.strftime("%z"), offsets["author-tz"])
798+
self.assertEqual(commit.committed_datetime.strftime("%z"), offsets["committer-tz"])
799+
776800
@mock.patch.object(Git, "_call_process")
777801
def test_blame_complex_revision(self, git):
778802
git.return_value = fixture("blame_complex_revision")

‎tests/fixtures/blame-content.sh‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,9 @@ cd repo
66
# CRLF line survive fixture creation on every platform.
77
blob=$(printf 'alpha \nbeta\n\t\ncrlf\r\ngamma\n' | git hash-object -w --stdin)
88
tree=$(printf '100644 blob %s\tfile.txt\0' "$blob" | git mktree -z)
9-
commit=$(git -c user.name=A -c user.email=a@example.com commit-tree "$tree" -m content)
9+
commit=$(GIT_AUTHOR_DATE='2020-01-01T12:00:00+0530' \
10+
GIT_COMMITTER_DATE='2020-01-01T13:00:00-0400' \
11+
git -c user.name=A -c user.email=a@example.com commit-tree "$tree" -m content)
1012
git update-ref refs/heads/main "$commit"
13+
git reset --hard -q HEAD
14+
printf 'changed\n' > file.txt

0 commit comments

Comments
 (0)