Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge folders with NSFileManager, overwrite only existing files

Basically I am looking for a way to merge two folder in the filesystem with the cocoa API:

I have a folder containing files and sub-folders, which I want to copy to a different location in the filesystem.
At my destination path, an equally-named folder already exists, which may contain files and folders as well.

Now I want to overwrite existing files inside my destination folder (or its subfolders) with the new content of my source folder if they have the same name.
All the rest of the files I want to leave untouched.

sourcefolder
   |
   - file1
   - subfolder
       - file2


destinationfolder
   |
   - file3
   - subfolder
       - file2
       - file4


resultingfolder
   |
   - file1
   - file3
   - subfolder
       - file2      <-- version from source folder
       - file4

How can I do that? Thanks a lot for your help!

like image 924
de. Avatar asked Sep 27 '13 15:09

de.


People also ask

What happens when you merge two folders?

When a folder is copied over another folder with the same name, Windows will automatically merge its contents, prompting you to resolve any file conflicts that arise. Here's how to do it: Locate the two folders you wish to merge. If their names aren't identical, rename one of them to match the other.

How do I merge two folders on my computer?

Select all of its content, you can use the keyboard shortcut Ctrl + A. Hit Ctrl + C to copy all the content that you selected earlier. Now, navigate to the other folder. Right-click on the blank space and select Paste or just hit Ctrl + V.


2 Answers

I searched everywhere but found nothing. So I came up with my own solution, utilizing NSDirectoryEnumerator. This should work the diagram (overriding old files). Hope it helps.

- (void)mergeContentsOfPath:(NSString *)srcDir intoPath:(NSString *)dstDir error:(NSError**)err {

    NSLog(@"- mergeContentsOfPath: %@\n intoPath: %@", srcDir, dstDir);

    NSFileManager *fm = [NSFileManager defaultManager];
    NSDirectoryEnumerator *srcDirEnum = [fm enumeratorAtPath:srcDir];
    NSString *subPath;
    while ((subPath = [srcDirEnum nextObject])) {

        NSLog(@" subPath: %@", subPath);
        NSString *srcFullPath =  [srcDir stringByAppendingPathComponent:subPath];
        NSString *potentialDstPath = [dstDir stringByAppendingPathComponent:subPath];

        // Need to also check if file exists because if it doesn't, value of `isDirectory` is undefined.
        BOOL isDirectory = ([[NSFileManager defaultManager] fileExistsAtPath:srcFullPath isDirectory:&isDirectory] && isDirectory);

        // Create directory, or delete existing file and move file to destination
        if (isDirectory) {
            NSLog(@"   create directory");
            [fm createDirectoryAtPath:potentialDstPath withIntermediateDirectories:YES attributes:nil error:err];
            if (err && *err) {
                NSLog(@"ERROR: %@", *err);
                return;
            }
        }
        else {
            if ([fm fileExistsAtPath:potentialDstPath]) {
                NSLog(@"   removeItemAtPath");
                [fm removeItemAtPath:potentialDstPath error:err];
                if (err && *err) {
                    NSLog(@"ERROR: %@", *err);
                    return;
                }
            }

            NSLog(@"   moveItemAtPath");
            [fm moveItemAtPath:srcFullPath toPath:potentialDstPath error:err];
            if (err && *err) {
                NSLog(@"ERROR: %@", *err);
                return;
            }
        }
    }
}
like image 108
Hlung Avatar answered Nov 15 '22 19:11

Hlung


A solution in Swift 3

let merger = FoldersMerger(actionType: .copy, conflictResolution: .keepSource)
merger.merge(atPath: sourceFolder, toPath: destinationFolder)


class FoldersMerger {

    enum ActionType { case move, copy }
    enum ConflictResolution { case keepSource, keepDestination }

    private let fileManager = FileManager()
    private var actionType: ActionType!
    private var conflictResolution: ConflictResolution!
    private var deleteEmptyFolders: Bool!

    init(actionType: ActionType = .move, conflictResolution: ConflictResolution = .keepDestination, deleteEmptyFolders: Bool = false) {
        self.actionType = actionType
        self.conflictResolution = conflictResolution
        self.deleteEmptyFolders = deleteEmptyFolders
    }

    func merge(atPath: String, toPath: String) {
        let pathEnumerator = fileManager.enumerator(atPath: atPath)

        var folders: [String] = [atPath]

        while let relativePath = pathEnumerator?.nextObject() as? String {

            let subItemAtPath = URL(fileURLWithPath: atPath).appendingPathComponent(relativePath).path
            let subItemToPath = URL(fileURLWithPath: toPath).appendingPathComponent(relativePath).path

            if isDir(atPath: subItemAtPath) {

                if deleteEmptyFolders! {
                   folders.append(subItemAtPath)
                }

                if !isDir(atPath: subItemToPath) {
                    do {
                        try fileManager.createDirectory(atPath: subItemToPath, withIntermediateDirectories: true, attributes: nil)
                        NSLog("FoldersMerger: directory created: %@", subItemToPath)
                    }
                    catch let error {
                        NSLog("ERROR FoldersMerger: %@", error.localizedDescription)
                    }
                }
                else {
                    NSLog("FoldersMerger: directory %@ already exists", subItemToPath)
                }
            }
            else {

                if isFile(atPath:subItemToPath) && conflictResolution == .keepSource {
                    do {
                        try fileManager.removeItem(atPath: subItemToPath)
                        NSLog("FoldersMerger: file deleted: %@", subItemToPath)
                    }
                    catch let error {
                        NSLog("ERROR FoldersMerger: %@", error.localizedDescription)
                    }
                }

                do {
                    try fileManager.moveItem(atPath: subItemAtPath, toPath: subItemToPath)
                    NSLog("FoldersMerger: file moved from %@ to %@", subItemAtPath, subItemToPath)
                }
                catch let error {
                    NSLog("ERROR FoldersMerger: %@", error.localizedDescription)
                }
            }
        }

        if deleteEmptyFolders! {
            folders.sort(by: { (path1, path2) -> Bool in
                return path1.characters.split(separator: "/").count < path2.characters.split(separator: "/").count
            })
            while let folderPath = folders.popLast() {
                if isDirEmpty(atPath: folderPath) {
                    do {
                        try fileManager.removeItem(atPath: folderPath)
                        NSLog("FoldersMerger: empty dir deleted: %@", folderPath)
                    }
                    catch {
                        NSLog("ERROR FoldersMerger: %@", error.localizedDescription)
                    }
                }
            }
        }
    }

    private func isDir(atPath: String) -> Bool {
        var isDir: ObjCBool = false
        let exist = fileManager.fileExists(atPath: atPath, isDirectory: &isDir)
        return exist && isDir.boolValue
    }

    private func isFile(atPath: String) -> Bool {
        var isDir: ObjCBool = false
        let exist = fileManager.fileExists(atPath: atPath, isDirectory: &isDir)
        return exist && !isDir.boolValue
    }

    private func isDirEmpty(atPath: String) -> Bool {
        do {
            return try fileManager.contentsOfDirectory(atPath: atPath).count == 0
        }
        catch _ {
            return false
        }
    }
}
like image 33
MathieuLescure Avatar answered Nov 15 '22 21:11

MathieuLescure