瀏覽代碼

Add more tests for handling of symlinks in porcelain.add

Jelmer Vernooij 2 月之前
父節點
當前提交
3e846b63e4
共有 1 個文件被更改,包括 166 次插入0 次删除
  1. 166 0
      tests/test_porcelain.py

+ 166 - 0
tests/test_porcelain.py

@@ -1047,6 +1047,172 @@ class AddTests(PorcelainTestCase):
         index = self.repo.open_index()
         self.assertIn(b"symlink_to_nowhere", index)
 
+    def test_add_symlink_to_file_inside_repo(self) -> None:
+        """Test adding a symlink that points to a file inside the repository."""
+        # Create a regular file
+        target_file = os.path.join(self.repo.path, "target.txt")
+        with open(target_file, "w") as f:
+            f.write("target content")
+        
+        # Create a symlink to the file
+        symlink_path = os.path.join(self.repo.path, "link_to_target")
+        os.symlink("target.txt", symlink_path)
+        
+        # Add both the target and the symlink
+        added, ignored = porcelain.add(self.repo.path, paths=[target_file, symlink_path])
+        
+        # Both should be added successfully
+        self.assertIn("target.txt", added)
+        self.assertIn("link_to_target", added)
+        self.assertEqual(len(ignored), 0)
+        
+        # Verify both are in the index
+        index = self.repo.open_index()
+        self.assertIn(b"target.txt", index)
+        self.assertIn(b"link_to_target", index)
+
+    def test_add_symlink_to_directory_inside_repo(self) -> None:
+        """Test adding a symlink that points to a directory inside the repository."""
+        # Create a directory with some files
+        target_dir = os.path.join(self.repo.path, "target_dir")
+        os.mkdir(target_dir)
+        with open(os.path.join(target_dir, "file1.txt"), "w") as f:
+            f.write("content1")
+        with open(os.path.join(target_dir, "file2.txt"), "w") as f:
+            f.write("content2")
+        
+        # Create a symlink to the directory
+        symlink_path = os.path.join(self.repo.path, "link_to_dir")
+        os.symlink("target_dir", symlink_path)
+        
+        # Add the symlink
+        added, ignored = porcelain.add(self.repo.path, paths=[symlink_path])
+        
+        # When adding a symlink to a directory, it follows the symlink and adds contents
+        self.assertEqual(len(added), 2)
+        self.assertIn("link_to_dir/file1.txt", added)
+        self.assertIn("link_to_dir/file2.txt", added)
+        self.assertEqual(len(ignored), 0)
+        
+        # Verify files are added through the symlink path
+        index = self.repo.open_index()
+        self.assertIn(b"link_to_dir/file1.txt", index)
+        self.assertIn(b"link_to_dir/file2.txt", index)
+        # The original target directory files are not added
+        self.assertNotIn(b"target_dir/file1.txt", index)
+        self.assertNotIn(b"target_dir/file2.txt", index)
+
+    def test_add_symlink_chain(self) -> None:
+        """Test adding a chain of symlinks (symlink to symlink)."""
+        # Create a regular file
+        target_file = os.path.join(self.repo.path, "original.txt")
+        with open(target_file, "w") as f:
+            f.write("original content")
+        
+        # Create first symlink
+        first_link = os.path.join(self.repo.path, "link1")
+        os.symlink("original.txt", first_link)
+        
+        # Create second symlink pointing to first
+        second_link = os.path.join(self.repo.path, "link2")
+        os.symlink("link1", second_link)
+        
+        # Add all files
+        added, ignored = porcelain.add(self.repo.path, paths=[target_file, first_link, second_link])
+        
+        # All should be added
+        self.assertEqual(len(added), 3)
+        self.assertIn("original.txt", added)
+        self.assertIn("link1", added)
+        self.assertIn("link2", added)
+        
+        # Verify all are in the index
+        index = self.repo.open_index()
+        self.assertIn(b"original.txt", index)
+        self.assertIn(b"link1", index)
+        self.assertIn(b"link2", index)
+
+    def test_add_broken_symlink(self) -> None:
+        """Test adding a broken symlink (points to non-existent target)."""
+        # Create a symlink to a non-existent file
+        broken_link = os.path.join(self.repo.path, "broken_link")
+        os.symlink("does_not_exist.txt", broken_link)
+        
+        # Add the broken symlink
+        added, ignored = porcelain.add(self.repo.path, paths=[broken_link])
+        
+        # Should be added successfully (Git tracks the symlink, not its target)
+        self.assertIn("broken_link", added)
+        self.assertEqual(len(ignored), 0)
+        
+        # Verify it's in the index
+        index = self.repo.open_index()
+        self.assertIn(b"broken_link", index)
+
+    def test_add_symlink_relative_outside_repo(self) -> None:
+        """Test adding a symlink that uses '..' to point outside the repository."""
+        # Create a file outside the repo
+        outside_file = os.path.join(self.test_dir, "outside.txt")
+        with open(outside_file, "w") as f:
+            f.write("outside content")
+        
+        # Create a symlink using relative path to go outside
+        symlink_path = os.path.join(self.repo.path, "link_outside")
+        os.symlink("../outside.txt", symlink_path)
+        
+        # Add the symlink
+        added, ignored = porcelain.add(self.repo.path, paths=[symlink_path])
+        
+        # Should be added successfully
+        self.assertIn("link_outside", added)
+        self.assertEqual(len(ignored), 0)
+        
+        # Verify it's in the index
+        index = self.repo.open_index()
+        self.assertIn(b"link_outside", index)
+
+    def test_add_symlink_absolute_to_system(self) -> None:
+        """Test adding a symlink with absolute path to system directory."""
+        # Create a symlink to a system directory
+        symlink_path = os.path.join(self.repo.path, "link_to_tmp")
+        os.symlink("/tmp", symlink_path)
+        
+        # Adding a symlink to a directory outside the repo should raise ValueError
+        with self.assertRaises(ValueError) as cm:
+            porcelain.add(self.repo.path, paths=[symlink_path])
+        
+        # Check that the error indicates the path is outside the repository
+        self.assertIn("is not in the subpath of", str(cm.exception))
+
+    def test_add_file_through_symlink(self) -> None:
+        """Test adding a file through a symlinked directory."""
+        # Create a directory with a file
+        real_dir = os.path.join(self.repo.path, "real_dir")
+        os.mkdir(real_dir)
+        real_file = os.path.join(real_dir, "file.txt")
+        with open(real_file, "w") as f:
+            f.write("content")
+        
+        # Create a symlink to the directory
+        link_dir = os.path.join(self.repo.path, "link_dir")
+        os.symlink("real_dir", link_dir)
+        
+        # Try to add the file through the symlink path
+        symlink_file_path = os.path.join(link_dir, "file.txt")
+        
+        # This should add the real file, not create a new entry
+        added, ignored = porcelain.add(self.repo.path, paths=[symlink_file_path])
+        
+        # The real file should be added
+        self.assertIn("real_dir/file.txt", added)
+        self.assertEqual(len(added), 1)
+        
+        # Verify correct path in index
+        index = self.repo.open_index()
+        self.assertIn(b"real_dir/file.txt", index)
+        # Should not create a separate entry for the symlink path
+        self.assertNotIn(b"link_dir/file.txt", index)
+
     def test_add_repo_path(self) -> None:
         """Test adding the repository path itself should add all untracked files."""
         # Create some untracked files