| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,7 +2,8 @@ | |||
| 2 | 2 | ||
| 3 | 3 | Before starting work, read and follow [CONTRIBUTING.md](CONTRIBUTING.md), | |
| 4 | 4 | including the [Prevent agent impersonation](CONTRIBUTING.md#prevent-agent-impersonation) | |
| 5 | - section governing identification when communicating through a person's account. | ||
| 5 | + section governing agent identification and separation of unaltered user statements | ||
| 6 | + when communicating through a person's account. | ||
| 6 | 7 | ||
| 7 | 8 | # Commit messages | |
| 8 | 9 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -27,6 +27,13 @@ example in issue or PR descriptions and comments. AI assistance that does not re | |||
| 27 | 27 | the person as the speaker, such as proofreading or wording polish, does not require | |
| 28 | 28 | identification. | |
| 29 | 29 | ||
| 30 | + Even when identifying themselves, agents must not assert on a person's behalf that | ||
| 31 | + that person performed an action, such as reviewing or approving a PR. The person | ||
| 32 | + must make any such statement themselves. If it is included alongside agent-authored | ||
| 33 | + content, it must be supplied by the person and preserved verbatim in a clearly | ||
| 34 | + labeled, separate user-authored section. Agents must not draft, paraphrase, or embed | ||
| 35 | + such statements in their own narration. | ||
| 36 | + | ||
| 30 | 37 | Attributing AI assistance in commit metadata, for example with a `Co-authored-by` | |
| 31 | 38 | trailer, is welcome but not required. Code is reviewed the same way regardless of its | |
| 32 | 39 | origin. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1940,9 +1940,15 @@ def _prepare_ref(self, ref: object) -> bytes: | |||
| 1940 | 1940 | else: | |
| 1941 | 1941 | refstr = ref | |
| 1942 | 1942 | ||
| 1943 | - if not refstr.endswith("\n"): | ||
| 1944 | - refstr += "\n" | ||
| 1945 | - return refstr.encode(defenc) | ||
| 1943 | + # A line feed terminates a request, so one object name must be one line. An | ||
| 1944 | + # embedded one would queue a second request on the persistent command while | ||
| 1945 | + # only one response line is read back, leaving every later call one response | ||
| 1946 | + # behind, answered with the header of an object it did not ask for. | ||
| 1947 | + if refstr.endswith("\n"): | ||
| 1948 | + refstr = refstr[:-1] | ||
| 1949 | + if "\n" in refstr: | ||
| 1950 | + raise ValueError("Object name %r contains a line feed" % refstr) | ||
| 1951 | + return (refstr + "\n").encode(defenc) | ||
| 1946 | 1952 | ||
| 1947 | 1953 | def _get_persistent_cmd(self, attr_name: str, cmd_name: str, *args: Any, **kwargs: Any) -> "Git.AutoInterrupt": | |
| 1948 | 1954 | cur_val = getattr(self, attr_name) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -525,17 +525,17 @@ def _write_path_to_stdin( | |||
| 525 | 525 | the piped-in files are processed anyway and just in time. | |
| 526 | 526 | ||
| 527 | 527 | :note: | |
| 528 | - Newlines are essential here, git's behaviour is somewhat inconsistent on | ||
| 529 | - this depending on the version, hence we try our best to deal with newlines | ||
| 530 | - carefully. Usually the last newline will not be sent, instead we will close | ||
| 531 | - stdin to break the pipe. | ||
| 528 | + Paths are NUL-terminated, so the command has to run with ``-z``. A path can | ||
| 529 | + contain a line feed, and with line-feed separation git would read such a | ||
| 530 | + path as two paths and act on files that were never passed. git also unquotes | ||
| 531 | + a line-feed separated path that begins with a double quote. | ||
| 532 | 532 | """ | |
| 533 | 533 | fprogress(filepath, False, item) | |
| 534 | 534 | rval: Union[None, str] = None | |
| 535 | 535 | ||
| 536 | 536 | if proc.stdin is not None: | |
| 537 | 537 | try: | |
| 538 | - proc.stdin.write(("%s\n" % filepath).encode(defenc)) | ||
| 538 | + proc.stdin.write(("%s\0" % filepath).encode(defenc)) | ||
| 539 | 539 | except OSError as e: | |
| 540 | 540 | # Pipe broke, usually because some error happened. | |
| 541 | 541 | raise fmakeexc() from e | |
@@ -1452,6 +1452,7 @@ def handle_stderr(proc: "Popen[bytes]", iter_checked_out_files: Iterable[PathLik | |||
| 1452 | 1452 | # initialization. | |
| 1453 | 1453 | self.entries # noqa: B018 | |
| 1454 | 1454 | ||
| 1455 | + args.append("-z") | ||
| 1455 | 1456 | args.append("--stdin") | |
| 1456 | 1457 | kwargs["as_process"] = True | |
| 1457 | 1458 | kwargs["istream"] = subprocess.PIPE | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -593,6 +593,19 @@ def test_persistent_cat_file_command(self): | |||
| 593 | 593 | self.assertEqual(typename, typename_two) | |
| 594 | 594 | self.assertEqual(size, size_two) | |
| 595 | 595 | ||
| 596 | + def test_object_header_rejects_an_embedded_line_feed(self): | ||
| 597 | + hexsha = "b2339455342180c7cc1e9bba3e9f181f7baa5167" | ||
| 598 | + git = Git(self.rorepo.working_dir) | ||
| 599 | + header = git.get_object_header(hexsha) | ||
| 600 | + | ||
| 601 | + # A single trailing line feed is the request terminator, not a second request. | ||
| 602 | + self.assertEqual(git.get_object_header(hexsha + "\n"), header) | ||
| 603 | + | ||
| 604 | + self.assertRaises(ValueError, git.get_object_header, "HEAD\nHEAD") | ||
| 605 | + | ||
| 606 | + # The persistent command is still in step, so this is not HEAD's header. | ||
| 607 | + self.assertEqual(git.get_object_header(hexsha), header) | ||
| 608 | + | ||
| 596 | 609 | def test_version_info(self): | |
| 597 | 610 | """The version_info attribute is a tuple of up to four ints.""" | |
| 598 | 611 | v = self.git.version_info | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1878,6 +1878,27 @@ def test_checkout_pathlike(self, tmp_path, path_type, absolute, directory, conta | |||
| 1878 | 1878 | for name, data in files.items(): | |
| 1879 | 1879 | assert (tmp_path / name).read_bytes() == data | |
| 1880 | 1880 | ||
| 1881 | + @pytest.mark.skipif(os.name == "nt", reason="Line feeds and quotes are not valid Windows filenames") | ||
| 1882 | + def test_checkout_sends_each_path_as_one_record(self, tmp_path): | ||
| 1883 | + with Repo.init(tmp_path) as repo: | ||
| 1884 | + nested = tmp_path / "nested" | ||
| 1885 | + nested.mkdir() | ||
| 1886 | + (nested / "first\noutside").write_bytes(b"nested") | ||
| 1887 | + (tmp_path / "outside").write_bytes(b"committed") | ||
| 1888 | + (tmp_path / '"quoted"').write_bytes(b"quoted") | ||
| 1889 | + repo.index.add(["nested", "outside", '"quoted"']) | ||
| 1890 | + | ||
| 1891 | + (nested / "first\noutside").unlink() | ||
| 1892 | + (tmp_path / '"quoted"').unlink() | ||
| 1893 | + (tmp_path / "outside").write_bytes(b"local") | ||
| 1894 | + | ||
| 1895 | + checked_out = {"nested/first\noutside", '"quoted"'} | ||
| 1896 | + assert set(repo.index.checkout(["nested", '"quoted"'], force=True)) == checked_out | ||
| 1897 | + assert (nested / "first\noutside").read_bytes() == b"nested" | ||
| 1898 | + assert (tmp_path / '"quoted"').read_bytes() == b"quoted" | ||
| 1899 | + # Neither "nested/first" nor "outside" was requested. | ||
| 1900 | + assert (tmp_path / "outside").read_bytes() == b"local" | ||
| 1901 | + | ||
| 1881 | 1902 | ||
| 1882 | 1903 | class TestIndexUtils: | |
| 1883 | 1904 | @pytest.mark.parametrize("file_path_type", [str, Path]) | |
| Back | FazBrowse Home | New Git URL |
0 commit comments