| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,6 +8,7 @@ | |||
| 8 | 8 | __all__ = ["Repo"] | |
| 9 | 9 | ||
| 10 | 10 | import gc | |
| 11 | + from io import BytesIO | ||
| 11 | 12 | import logging | |
| 12 | 13 | import os | |
| 13 | 14 | import os.path as osp | |
@@ -1358,8 +1359,10 @@ class InfoTD(TypedDict, total=False): | |||
| 1358 | 1359 | ||
| 1359 | 1360 | info: InfoTD = {} | |
| 1360 | 1361 | ||
| 1361 | - keepends = True | ||
| 1362 | - for line_bytes in data.splitlines(keepends): | ||
| 1362 | + # Git ends each porcelain line with a line feed only. bytes.splitlines() also | ||
| 1363 | + # splits at a lone carriage return, which a line of file content can contain, | ||
| 1364 | + # and would then parse the rest of that content line as blame headers. | ||
| 1365 | + for line_bytes in BytesIO(data): | ||
| 1363 | 1366 | line_str = "" | |
| 1364 | 1367 | try: | |
| 1365 | 1368 | line_str = line_bytes.rstrip().decode(defenc) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -839,9 +839,44 @@ def test_blame_complex_revision(self, git): | |||
| 839 | 839 | def test_blame_accepts_rev_opts(self, git): | |
| 840 | 840 | expected_args = ["blame", "HEAD", "-M", "-C", "-C", "--", "README.md"] | |
| 841 | 841 | boilerplate_kwargs = {"p": True, "stdout_as_string": False} | |
| 842 | + git.return_value = b"" | ||
| 842 | 843 | self.rorepo.blame("HEAD", "README.md", rev_opts=["-M", "-C", "-C"]) | |
| 843 | 844 | git.assert_called_once_with(*expected_args, **boilerplate_kwargs) | |
| 844 | 845 | ||
| 846 | + @with_rw_directory | ||
| 847 | + def test_blame_content_with_carriage_returns(self, rw_dir): | ||
| 848 | + repo = Repo.init(rw_dir) | ||
| 849 | + with repo.config_writer() as writer: | ||
| 850 | + writer.set_value("user", "name", "GitPython Tests") | ||
| 851 | + writer.set_value("user", "email", "gitpython@example.com") | ||
| 852 | + | ||
| 853 | + # A lone CR inside a line of content, followed by text shaped like blame | ||
| 854 | + # porcelain headers for a forged commit and author. | ||
| 855 | + forged = "\r".join( | ||
| 856 | + [ | ||
| 857 | + "ok", | ||
| 858 | + "0" * 40 + " 2 2 1", | ||
| 859 | + "author Forged Author", | ||
| 860 | + "author-mail <forged@example.com>", | ||
| 861 | + "author-time 1000000000", | ||
| 862 | + "committer Forged Author", | ||
| 863 | + "committer-mail <forged@example.com>", | ||
| 864 | + "committer-time 1000000000", | ||
| 865 | + "\tforged()", | ||
| 866 | + ] | ||
| 867 | + ) | ||
| 868 | + tracked_file = Path(rw_dir) / "file.txt" | ||
| 869 | + tracked_file.write_bytes(b"first\n" + forged.encode("ascii") + b"\n") | ||
| 870 | + repo.index.add([str(tracked_file)]) | ||
| 871 | + commit = repo.index.commit("init") | ||
| 872 | + | ||
| 873 | + blames = repo.blame("HEAD", "file.txt") | ||
| 874 | + | ||
| 875 | + self.assertEqual(len(blames), 1) | ||
| 876 | + self.assertEqual(blames[0][0], commit) | ||
| 877 | + self.assertEqual(blames[0][0].author.name, "GitPython Tests") | ||
| 878 | + self.assertEqual(blames[0][1], ["first", forged]) | ||
| 879 | + | ||
| 845 | 880 | @with_rw_repo("HEAD", bare=False) | |
| 846 | 881 | def test_untracked_files(self, rwrepo): | |
| 847 | 882 | for run, repo_add in enumerate((rwrepo.index.add, rwrepo.git.add)): | |
| Back | FazBrowse Home | New Git URL |
0 commit comments