{"record":{"id":"de0b90b56728de8e","repo":"ungoogled-software/ungoogled-chromium","slug":"patches-from-source-dir-have-conflicting-paths-w","errorCode":null,"errorMessage":"Patches from {source_dir} have conflicting paths with other sources: {patch_intersection}","messagePattern":"Patches from (.+?) have conflicting paths with other sources: (.+?)","errorType":"exception","errorClass":"FileExistsError","httpStatus":null,"severity":"error","filePath":"utils/patches.py","lineNumber":178,"sourceCode":"\n    destination must not already exist, unless prepend is True. If prepend is True, then\n    the source patches will be prepended to the destination.\n    \"\"\"\n    series = []\n    known_paths = set()\n    if destination.exists():\n        if prepend:\n            if not (destination / 'series').exists():\n                raise FileNotFoundError(\n                    f\"Could not find series file in existing destination: {destination / 'series'}\")\n            known_paths.update(generate_patches_from_series(destination))\n        else:\n            raise FileExistsError(f'destination already exists: {destination}')\n    for source_dir in source_iter:\n        patch_paths = tuple(generate_patches_from_series(source_dir))\n        patch_intersection = known_paths.intersection(patch_paths)\n        if patch_intersection:\n            raise FileExistsError(f'Patches from {source_dir} have conflicting paths '\n                                  f'with other sources: {patch_intersection}')\n        series.extend(patch_paths)\n        _copy_files(patch_paths, source_dir, destination)\n    if prepend and (destination / 'series').exists():\n        series.extend(generate_patches_from_series(destination))\n    with (destination / 'series').open('w') as series_file:\n        series_file.write('\\n'.join(map(str, series)))\n\n\ndef _apply_callback(args, parser_error):\n    logger = get_logger()\n    patch_bin_path = None\n    if args.patch_bin is not None:\n        patch_bin_path = Path(args.patch_bin)\n        if not patch_bin_path.exists():\n            patch_bin_path = shutil.which(args.patch_bin)\n            if patch_bin_path:\n                patch_bin_path = Path(patch_bin_path)","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/ungoogled-software/ungoogled-chromium/blob/f85e84a480e2e17c103de7013de94320f9a2ba39/utils/patches.py#L160-L196","documentation":"merge_patches raises FileExistsError when two source patch directories contain patch files at the same relative path (per their series files). Overlapping patches would silently overwrite each other during copy, so the merge aborts and lists the conflicting paths.","triggerScenarios":"Calling merge_patches / merge_platform_patches where generate_patches_from_series(source_dir) for two different source_dirs yields identical patch paths; also when prepending into an existing destination whose series already lists the same patch names as an incoming source.","commonSituations":"Merging two platform patch sets that both vendor a common patch (e.g. 'fix-common/0001-base.patch'); including the same source directory twice in sources; upstream refactor renamed dirs so distinct patches now share filenames.","solutions":["Remove or rename the duplicated patch in one of the source directories","Drop the duplicate source directory from the source_iter list","Namespace patch paths per source (e.g. put patches under a platform-specific subdirectory) so relative paths are unique","Diff the series files of the sources to identify and reconcile the overlapping entries before merging"],"exampleFix":"# before\nsources = [platform_a_patches, platform_b_patches]  # both contain common/0001-fix.patch\nmerge_patches(sources, dest)\n# after\nsources = [platform_a_patches, platform_b_patches]\n# rename one side: platform_b_patches/common/0001-fix.patch -> platform_b_patches/b/0001-fix.patch\nmerge_patches(sources, dest)","handlingStrategy":"validation","validationCode":"from pathlib import Path\nfrom utils.patches import generate_patches_from_series\nseen = set()\nfor src in source_dirs:\n    paths = set(generate_patches_from_series(Path(src)))\n    dupes = seen & paths\n    assert not dupes, f'conflicting patch paths across sources: {dupes}'\n    seen |= paths","typeGuard":"def sources_are_disjoint(source_dirs):\n    seen = set()\n    for src in source_dirs:\n        paths = set(generate_patches_from_series(Path(src)))\n        if seen & paths:\n            return False\n        seen |= paths\n    return True","tryCatchPattern":"try:\n    merge_patches(sources, dest)\nexcept FileExistsError as e:\n    logger.error('conflicting patch paths: %s', e)\n    # inspect e for the listed intersection and de-duplicate sources before retrying","preventionTips":["Namespace each source's patches under a unique subdirectory","Keep a single source of truth for shared/common patches","Deduplicate source_dirs before merging","Diff series files across sources in CI to catch overlaps early"],"tags":["file-exists","patch-conflict","merge","duplicate-paths"],"backgroundTag":"patch-path-conflict","analyzedSha":"f85e84a480e2e17c103de7013de94320f9a2ba39","analyzedAt":"2026-08-29T10:07:17.606Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}