Parcourir la source

Apply filters when checking file modifications in remove command

The remove command checks if files have been modified before removing
them from the index. However, it was comparing the raw file content
against the normalized index content, which could lead to false
positives when line endings differ.

Now the remove command applies checkin normalization to the file content
before comparing it with the index, ensuring accurate modification
detection regardless of line ending differences in the working tree.
Jelmer Vernooij il y a 1 mois
Parent
commit
af677125a3
2 fichiers modifiés avec 44 ajouts et 0 suppressions
  1. 5 0
      dulwich/porcelain.py
  2. 39 0
      tests/test_porcelain.py

+ 5 - 0
dulwich/porcelain.py

@@ -808,6 +808,8 @@ def remove(repo=".", paths=None, cached=False) -> None:
     """
     with open_repo_closing(repo) as r:
         index = r.open_index()
+        blob_normalizer = r.get_blob_normalizer()
+
         for p in paths:
             # If path is absolute, use it as-is. Otherwise, treat it as relative to repo
             if os.path.isabs(p):
@@ -831,6 +833,9 @@ def remove(repo=".", paths=None, cached=False) -> None:
                 else:
                     try:
                         blob = blob_from_path_and_stat(full_path_bytes, st)
+                        # Apply checkin normalization to compare apples to apples
+                        if blob_normalizer is not None:
+                            blob = blob_normalizer.checkin_normalize(blob, tree_path)
                     except OSError:
                         pass
                     else:

+ 39 - 0
tests/test_porcelain.py

@@ -1876,6 +1876,45 @@ class RemoveTests(PorcelainTestCase):
         # Verify file was removed
         self.assertFalse(os.path.exists(fullpath))
 
+    def test_remove_with_filter_normalization(self) -> None:
+        # Enable autocrlf to normalize line endings
+        config = self.repo.get_config()
+        config.set(("core",), "autocrlf", b"true")
+        config.write_to_path()
+
+        # Create a file with LF line endings (will be stored with LF in index)
+        fullpath = os.path.join(self.repo.path, "foo.txt")
+        with open(fullpath, "wb") as f:
+            f.write(b"line1\nline2\nline3")
+
+        # Add and commit the file (stored with LF in index)
+        porcelain.add(self.repo.path, paths=[fullpath])
+        porcelain.commit(
+            repo=self.repo,
+            message=b"Add file with LF",
+            author=b"test <email>",
+            committer=b"test <email>",
+        )
+
+        # Simulate checkout with CRLF conversion (as would happen on Windows)
+        with open(fullpath, "wb") as f:
+            f.write(b"line1\r\nline2\r\nline3")
+
+        # Verify file exists
+        self.assertTrue(os.path.exists(fullpath))
+
+        # Remove the file - this should not fail even though working tree has CRLF
+        # and index has LF (thanks to the normalization in the commit)
+        cwd = os.getcwd()
+        try:
+            os.chdir(self.repo.path)
+            porcelain.remove(self.repo.path, paths=["foo.txt"])
+        finally:
+            os.chdir(cwd)
+
+        # Verify file was removed
+        self.assertFalse(os.path.exists(fullpath))
+
 
 class MvTests(PorcelainTestCase):
     def test_mv_file(self) -> None: