@@ -18,10 +18,12 @@ class FileItems:
1818 files : set [Path ] = field (default_factory = set )
1919
2020 def add (self , other : "FileItems" ):
21+ """Merge in another set of files/folders."""
2122 self .folders |= other .folders
2223 self .files |= other .files
2324
2425 def subtract (self , other : "FileItems" ):
26+ """Remove files/folders that occur in another set."""
2527 self .folders = self .folders .difference (other .folders )
2628 self .files = self .files .difference (other .files )
2729
@@ -178,6 +180,8 @@ def operation_merge(
178180 """See help info for `merge`."""
179181 new_sources = FileItems .merge (new_sources .values ())
180182
183+ self .skip_file_duplicates (new_sources , current_sources )
184+
181185 sizes_all = (len (new_sources .folders ), len (new_sources .files ))
182186
183187 new_sources .subtract (current_sources )
@@ -251,6 +255,8 @@ def operation_reset(self, current_sources: FileItems, new_sources: FileItemsGrou
251255 to_remove .subtract (to_add ) # Don't remove sources that are real
252256 to_add .subtract (current_sources ) # Don't add sources that are already known
253257
258+ self .skip_file_duplicates (to_add , current_sources )
259+
254260 # Empty folders will also be removed, make sure to keep them:
255261 to_remove .folders = set (
256262 f for f in to_remove .folders if not (self ._project_file .parent / f ).exists ()
@@ -268,7 +274,7 @@ def operation_reset(self, current_sources: FileItems, new_sources: FileItemsGrou
268274 )
269275
270276 # Decide what to do next:
271- if not to_remove .is_empty () and to_add .is_empty ():
277+ if to_remove .is_empty () and to_add .is_empty ():
272278 self .logger .info ("No files or folders to change, stopping" )
273279 return 0
274280
@@ -312,6 +318,25 @@ def determine_source_folders(self, source_files: Iterable[Path]) -> FileItems:
312318
313319 return sources
314320
321+ def skip_file_duplicates (
322+ self ,
323+ new_sources : FileItems ,
324+ current_sources : FileItems ,
325+ ):
326+ """Avoid duplicate file names (regardless of full path).
327+
328+ ``current_sources`` is modified in-place.
329+ """
330+ filenames = set (f .name for f in current_sources .files ) # Reduce to just names
331+ to_pop = set ()
332+ for file in new_sources .files :
333+ if file .name in filenames :
334+ to_pop .add (file )
335+ self .logger .warning (f"Refusing to add existing file name: { file } " )
336+
337+ if to_pop :
338+ new_sources .files -= to_pop
339+
315340 def get_project_sources (self , tree ) -> FileItems :
316341 """Get all files and folders currently in a PLC project."""
317342
0 commit comments