How to use Git and Dropbox together

August 30, 2018

Problem: You and some colleagues are working on a new project together (e.g. writing a new paper). You use Git to version-control your work and sync it to the cloud (with GitHub or similar), but your colleagues use Dropbox.

What to do? Abandon Git when collaborating? (No!) Force your collaborators to learn Git? (Ye… No!)

Solution: Use both! All you need to do is tell Dropbox to ignore all Git-specific files, and tell Git to ignore all Dropbox-specific files. While that sounds pretty simple, there are a few important caveats to watch out for. Here’s how to do it.

Step 1: Get the Dropbox command-line program

The first thing you’ll need is the Dropbox command-line program. Depending on your Linux distribution, this program may already come with your Dropbox package, or it may be available as a separate package (on Arch Linux, the package is separate and is called dropbox-cli). Or you can just download the Python script directly. For concreteness, I’m going to call this program dropbox, but on your machine it may be called dropbox.py, dropbox-cli (Arch Linux), etc.

dropbox provides a number of commands. Run dropbox help to see them all.

The one we care about is the exclude command, which allows you to exclude specific files/directories from sync’ing to Dropbox.

You can run dropbox exclude list to list all excluded files, dropbox exclude add [file] ... to add a file to the exclude list, or dropbox exclude remove [file] ... to remove a file from the exclude list.

By the way, even though dropbox help exclude mentions only directories, you can actually exclude both directories and plain files. (That’s probably because on Unix, everything is a file.)

See this article on the Dropbox wiki for more information.

Step 2: Start Dropbox

At this point, you can already start Dropbox. So, in a terminal, run:

$ dropbox start

Protip: Use the command

$ dropbox start && watch -n1 dropbox status && dropbox stop

to continually (every second) watch the Dropbox status, or better yet, create an alias for it called db, as I previously described in this post.

Step 3: Start a new project

Now we start our new project, e.g. LaTeX manuscript. (If you used the db alias/command above, then fire up a new terminal.) Let’s call the project project, because we’re not creative:

$ mkdir ~/Dropbox/project
$ cd ~/Dropbox/project

From here on out, I’m going to assume that every command is run from inside ~/Dropbox/project.

Step 4: Exclude Git files from Dropbox sync

With Dropbox running, we’re going to preemptively exclude two Git-related things: the directory .git and the file .gitignore, both of which will be created in the next step.

# Run this from inside ~/Dropbox/project!
$ dropbox exclude add .git .gitignore
Excluded:
.git
.gitignore

Dropbox confirms that it excluded the files, but you can double-check that it worked by running:

$ dropbox exclude list
Excluded:
.git
.gitignore

Note that .git and .gitignore don’t even exist yet! In other words, you can tell Dropbox to exclude stuff before even creating it. In fact, you should do this. More about that at the end.

Step 5: Exclude Dropbox files from Git tracking

Now we turn project into a Git repository in the usual way:

$ git init

This creates the directory .git. Since we previously excluded this directory from Dropbox sync, Dropbox won’t actually do anything here.

If you check Git’s status:

$ git status

you may see an untracked file called .dropbox. We want Git to ignore that, so we create the file .gitignore and add .dropbox to it:

$ echo ".dropbox" > .gitignore

Again, since we previously excluded .gitignore, Dropbox still won’t do anything.

Now, when you run git status, you won’t see .dropbox.

Note: This step may no longer be necessary. When I tested this just now, Dropbox did not create a .dropbox file, and all the .dropbox files in my Dropbox are many months old. So it may be that the newest version of Dropbox no longer creates a .dropbox file (or that the .dropbox file is coming from one of my collaborators’ versions of Dropbox). Still, it can’t hurt to add it to your .gitignore.

Step 6: Profit

Now you can work as usual, sync’ing to Dropbox and committing changes to Git as you normally would. The two will not step on each other’s toes.

The really nice thing about this method is that when you sync your collaborators’ changes to your machine over Dropbox, you can run

$ git status

to see which files were added, deleted, or changed, as well as

$ git diff

or (my preference)

$ git diff --word-diff=color

to see exactly what changes were made by your collaborators since your last Git commit.

Protip: Before doing new work on your project, and definitely before sync’ing over Dropbox, I highly recommend checking https://www.dropbox.com/events to see if anyone has made any changes. If so, then pull in the changes first, review and commit them with Git:

$ git commit -am "Merge Alice's changes"

and then start your work. (Otherwise, Dropbox will create a conflicted copy, and then you’ll have to manually merge your conflicted copy with your collaborator’s copy.)

Important warning!

For some reason that I don’t understand at all, if you run:

$ dropbox exclude add foo

and foo already exists, then Dropbox will actually delete foo! Therefore, if you decide you want to exclude a file from sync’ing over Dropbox, but it’s a file that you want to keep locally, be sure to move the file out of your project folder first (or make a copy of it).

For example, let’s say that I’ve created a file notes that contains my own notes for a collaborative project. My colleagues don’t care to see that, so I decide to exclude it:

# From inside ~/Dropbox/project!
$ mv notes /tmp                # move notes to /tmp
$ dropbox start                # start Dropbox, if not already running
$ dropbox exclude add notes    # exclude notes
$ mv /tmp/notes .              # move notes back to project

If you accidentally exclude add a file that already exists, and Dropbox deletes it, don’t worry: you can manually download the deleted file by going to the Dropbox website, navigating to your project, and making sure to “Show deleted files”. Once you’ve downloaded the deleted file, move it into your project directory, and Dropbox won’t do anything with it, since it’s been excluded.

Step 7 (bonus): Multiple versions of a file or directory

Since your colleagues don’t use Git, they may be in the habit of creating entirely new files or directories when they implement (major) changes, which on first glance may look disastrous.

For example, suppose your project starts with the following state:

$ ls -a project
./ ../ .git/ .gitignore paper.tex paper.pdf

You start Dropbox, and then when you run git status, you notice that all your files have been deleted, and in their place you now have two untracked directories!

$ git status
Changes not staged for commit:

    deleted: paper.tex

Untracked files:

    v1/
    v2/

What happened, of course, was that your colleague moved the old paper.tex and paper.pdf into a new directory called v1, copied them into another new directory called v2, and then made their changes in the v2 version.

$ ls -a project
./ ../ .git/ .gitignore v1/ v2/
$ ls -a v1 v2
v1:
./  ../  paper.pdf  paper.tex

v2:
./  ../  paper.pdf  paper.tex

Assuming they haven’t made any filename changes, all you have to do is move .git and .gitignore from the top-level project directory to the new v2 directory, and then run git status to see the new changes.

However, before you do that, make sure to exclude v2/.git and v2/.gitignore!

$ dropbox exclude add v2/.git v2.gitignore
Excluded:
v2/.git
v2/.gitignore
$ mv .git .gitignore v2
$ cd v2
$ git status

You can also now un-exclude the old top-level Git files:

$ cd .. # move back to ~/Dropbox/project
$ dropbox exclude remove .git .gitignore
No longer excluded:
.git
.gitignore