How to fix Dropbox conflicts automatically

June 16, 2018 |Source Code

If you use Dropbox regularly, you have probably managed to screw it up at some point by modifying a file on two machines without syncing it between edits. When this happens, Dropbox will save both versions of the file and rename one of them like so:

manifesto.txt
manifesto (Work Macbook's conflicted copy 2014-10-22).txt 

Sometimes these files are genuinely different and need to be reconciled manually, but more than a few times I've ended up with conflicts where Dropbox is just confused into thinking files are new when they're not. (This tends to happen if I'm working offline for an extended period on my laptop while my Desktop at work is still online.) In that case, I just want to find every pair of duplicated files and keep the newer one.

You can do this manually for small conflicts. For deep file structures like .git directories, it becomes a painful way to spend a Thursday night (if a rather effective way to learn about the Git file structure).

Using this Stack Overflow post as a starting point, I wrote a Bash script to find every conflicted file in a Dropbox directory and resolve it to the newer of the two files, backing up the older one.

Buyer's beware: It's never a great idea to download Bash scripts from strangers and just run them. I've attempted to save you from yourself by making this script execute in "dry run" mode by default, but you should look over this script before running it.

Update, June 16, 2018: Several months ago, GitHub user @rswgnu cleaned up the code with comments, a streamlined output and an error catch. I've replaced the original script with his. Thank you, Robert!

# get the script
git clone https://gist.github.com/9d35f1972a33ca13f6be7330d2d01b7e.git
cd 9d35f1972a33ca13f6be7330d2d01b7e
# make it executable
chmod 700 dropbox-fix-conflicts
# execute it, but AFTER reading this post
./dropbox-fix-conflicts

If you look at the script, you'll see you need to point it manually to your Dropbox instance and choose a place to backup the files that will be moved out of the Dropbox directory and store any errors. The script will create this path for you if it doesn't exist.

Point to where you want the script to look and where it should backup files it replaces, on lines 12, 15, 18 and 21:

# Location of your local Dropbox folder
dropbox=~/Dropbox
# Dropbox folder in which to resolve conflicts
folder=~/Dropbox
# Where to move older files that are replaced during conflict resolution
backup=~/Dropbox-saved-files
# Where to store any errors that occur during moves and copies
errors=~/Dropbox-conflict-errors

The bulk of the script just scans every file and identifies each one that matches the Dropbox naming convention for conflicted files. It then compares the conflicted version and the regular one and keeps the newer one (removing the conflict language in the filename, of course).

As you see, the script doesn't actually move or name anything unless you pass "replace" as the first argument to the script. Otherwise, it's just a dry run.

find $folder -type f -print0 | while read -d $'\0' file; do
    newname=$(echo "$file" | sed 's/ (.*conflicted copy.*)//')

    if [ "$file" != "$newname" ]; then
        if [ -f "$newname" ]; then
            # determine which is newer
            if [ "$newname" -nt "$file" ]; then
             # echo -n "$newname is NEWER than $file; "
                file_to_backup="$file"
                file_to_keep="$newname"
            else
                # echo -n "$newname is OLDER than $file; "
                file_to_backup="$newname"
                file_to_keep="$file"
            fi

            backupname=${newname/"$dropbox"/"$backup"}

        n=$(basename "$newname"); m=$(basename "$file_to_backup"); bd=$(dirname "$backupname")
            if [[ $1 != "replace" ]]; then
                echo "Would have moved \"$file_to_keep\" to \"$n\" and backed up older original \"$m\"."
            else
                (mkdir -p "$bd" && cp -p "$file_to_backup" "$backupname" && mv "$file_to_keep" "$newname" && \
                  echo "Moved \"$file_to_keep\" to \"$n\" and backed up older original \"$m\".") 2> $errors
            fi
        else
            # If the unconflicted version isn't there for some reason, just rename the conflicted file to the original name.
            if [[ $1 != "replace" ]]; then
                echo "Would have moved conflicted file to '$file'."
            else
                echo "Moved \"$file\" to \"$n\"."
                mv "$file" "$newname" 2> $errors
            fi
        fi
    fi
done

Robert's improved Gist is here, and further edits are welcome. If you're ready to run, just say ./dropbox-fix-conflicts replace