Skip to main content

How to Merge Multiple Folders into One Folder Using Python

When managing files on your computer, you may need to consolidate the contents of multiple folders into a single destination folder. This is common when organizing downloaded files, combining project assets, or restructuring directory layouts. Doing this manually is tedious and error-prone, especially when dealing with dozens of folders containing hundreds of files.

In this guide, you'll learn how to automate this process using Python's built-in os and shutil modules, handle edge cases like duplicate filenames, and build a clean, reusable solution.

Prerequisites

This guide uses only Python's built-in modules: no external packages are required:

  • os: for navigating directories, listing files, and creating folders.
  • shutil: for moving files and folders between locations.

Understanding the Approach

The strategy for merging multiple folders is straightforward:

  1. Identify the source folders you want to merge.
  2. Collect the contents (files and subfolders) of each source folder.
  3. Create the destination folder if it doesn't already exist.
  4. Move all contents from each source folder into the destination.

Step-by-Step Implementation

Step 1: List Source Folders and Their Contents

First, define which folders you want to merge and gather their contents:

import os

# Working directory
current_folder = os.getcwd()

# Folders to merge
list_dir = ['Folder 1', 'Folder 2', 'Folder 3']

# Collect the contents of each folder
content_list = {}
for folder_name in list_dir:
folder_path = os.path.join(current_folder, folder_name)
content_list[folder_name] = os.listdir(folder_path)

# Display what was found
for folder, files in content_list.items():
print(f"{folder}: {files}")

Output (example):

Folder 1: ['File1.txt', 'File2.txt']
Folder 2: ['File3.txt', 'File4.txt']
Folder 3: ['File5.txt', 'File6.txt']

Step 2: Create the Destination Folder

Create the merge destination folder if it doesn't already exist. Using os.makedirs() with exist_ok=True is the cleanest approach:

merge_folder = "merge_folder"
merge_folder_path = os.path.join(current_folder, merge_folder)

# Create the folder (no error if it already exists)
os.makedirs(merge_folder_path, exist_ok=True)
print(f"Destination folder ready: {merge_folder_path}")
tip

Use os.makedirs(path, exist_ok=True) instead of manually checking with os.path.exists() or catching OSError. It's more concise and handles nested directory creation automatically.

Step 3: Move All Contents to the Destination

Loop through each source folder and move its contents into the destination:

import shutil

for folder_name, contents in content_list.items():
for item in contents:
source_path = os.path.join(current_folder, folder_name, item)
destination_path = os.path.join(merge_folder_path, item)
shutil.move(source_path, destination_path)
print(f"Moved: {source_path}{destination_path}")

Output (example):

Moved: /home/user/Folder 1/File1.txt → /home/user/merge_folder/File1.txt
Moved: /home/user/Folder 1/File2.txt → /home/user/merge_folder/File2.txt
Moved: /home/user/Folder 2/File3.txt → /home/user/merge_folder/File3.txt
...

Complete Code

Here's the full, clean implementation:

import os
import shutil


def merge_folders(source_folders, destination_folder, base_path=None):
"""
Merge the contents of multiple source folders into a single destination folder.

Args:
source_folders: List of folder names to merge.
destination_folder: Name of the destination folder.
base_path: Base directory path. Defaults to current working directory.
"""
if base_path is None:
base_path = os.getcwd()

destination_path = os.path.join(base_path, destination_folder)

# Create destination folder if it doesn't exist
os.makedirs(destination_path, exist_ok=True)

# Collect contents of each source folder
content_map = {}
for folder in source_folders:
folder_path = os.path.join(base_path, folder)
if os.path.isdir(folder_path):
content_map[folder] = os.listdir(folder_path)
else:
print(f"Warning: '{folder}' does not exist, skipping.")

# Move all contents to the destination
moved_count = 0
for folder_name, contents in content_map.items():
for item in contents:
source = os.path.join(base_path, folder_name, item)
destination = os.path.join(destination_path, item)
shutil.move(source, destination)
moved_count += 1

print(f"Done! Moved {moved_count} items into '{destination_folder}'.")


# Define source folders and destination
source_folders = ['Folder 1', 'Folder 2', 'Folder 3']
destination = 'merge_folder'

# Run the merge
merge_folders(source_folders, destination)

Folder Structure Before Running

project/
├── Folder 1/
│ ├── File1.txt
│ └── File2.txt
├── Folder 2/
│ ├── File3.txt
│ └── File4.txt
├── Folder 3/
│ ├── File5.txt
│ └── File6.txt
├── Folder 4/
│ ├── File7.txt
│ └── File8.txt
└── merge_script.py

Folder Structure After Running

project/
├── Folder 1/ (empty)
├── Folder 2/ (empty)
├── Folder 3/ (empty)
├── Folder 4/ (untouched)
│ ├── File7.txt
│ └── File8.txt
├── merge_folder/
│ ├── File1.txt
│ ├── File2.txt
│ ├── File3.txt
│ ├── File4.txt
│ ├── File5.txt
│ └── File6.txt
└── merge_script.py

Notice that Folder 4 is untouched because it wasn't included in the source_folders list.

Handling Duplicate Filenames

A common issue arises when multiple source folders contain files with the same name. By default, shutil.move() will overwrite the existing file in the destination without warning.

# ❌ If Folder 1 and Folder 2 both contain "report.txt",
# the second move silently overwrites the first one
shutil.move("Folder 1/report.txt", "merge_folder/report.txt")
shutil.move("Folder 2/report.txt", "merge_folder/report.txt") # Overwrites!

To avoid data loss, rename duplicates before moving:

import os
import shutil


def safe_move(source, destination_dir):
"""
Move a file to the destination directory, renaming it if a
file with the same name already exists.
"""
filename = os.path.basename(source)
destination = os.path.join(destination_dir, filename)

# If a file with the same name exists, add a counter suffix
if os.path.exists(destination):
name, ext = os.path.splitext(filename)
counter = 1
while os.path.exists(destination):
new_filename = f"{name}_{counter}{ext}"
destination = os.path.join(destination_dir, new_filename)
counter += 1
print(f"Renamed: '{filename}' → '{os.path.basename(destination)}'")

shutil.move(source, destination)


# Usage
safe_move("Folder 1/report.txt", "merge_folder")
safe_move("Folder 2/report.txt", "merge_folder") # Saved as report_1.txt

Output:

Renamed: 'report.txt' → 'report_1.txt'
caution

Always account for duplicate filenames when merging folders. Silent overwrites can cause permanent data loss. Use the safe_move() function above or check for conflicts before moving.

Copying Instead of Moving

If you want to keep the original files in their source folders and only create copies in the destination, use shutil.copy2() instead of shutil.move():

import shutil

# Copy instead of move (preserves metadata like timestamps)
shutil.copy2(source_path, destination_path)
shutil.copy2() vs shutil.copy()
  • shutil.copy() copies the file content and permissions.
  • shutil.copy2() additionally preserves the file's metadata (modification time, creation time, etc.).

Use copy2() when you want an exact replica of the original file.

Cleaning Up Empty Source Folders

After moving all files, the source folders will be empty. You can optionally remove them:

import os

source_folders = ['Folder 1', 'Folder 2', 'Folder 3']

for folder in source_folders:
folder_path = os.path.join(os.getcwd(), folder)
try:
os.rmdir(folder_path) # Only removes empty directories
print(f"Removed empty folder: {folder}")
except OSError:
print(f"Skipped '{folder}' (not empty)")
tip

os.rmdir() only removes empty directories, so it's safe to call: it won't accidentally delete folders that still contain files.

Summary

TaskFunction
List folder contentsos.listdir()
Create destination folderos.makedirs(path, exist_ok=True)
Move filesshutil.move()
Copy files (keep originals)shutil.copy2()
Remove empty foldersos.rmdir()

Merging multiple folders into one using Python is a straightforward process: list the source folders, collect their contents, create the destination directory, and move everything over. Always handle duplicate filenames to prevent data loss, and consider using shutil.copy2() instead of shutil.move() if you want to preserve the original files. Wrap the logic in a reusable function to keep your code clean and maintainable.