[git] How to use Git and Dropbox together?

Is it possible to use Git and Dropbox together?

This question is related to git dropbox

The answer is


Now in 2014, I have been using Git and Dropbox for about one year and a half without problem. Some points though:

  • All my machines using Dropbox are on Windows, different versions (7 to 8) + 1 mac.
  • I do not share the repository with someone else, so I am the only one to modify it.
  • git push pushes to a remote repository, so that if it ever gets corrupted, I can easily recover it.
  • I had to create aliases in C:\Users with mklink /D link target because some libraries were pointed to absolute locations.

There's also an open source project (a collection of cross platform [Linux, Mac, Win] scripts) that does all the nitty-gritty details of the repository management with a handful (3-4) of commands.

https://github.com/karalabe/gitbox/wiki

Sample usage is:

$ gitbox create myapp
Creating empty repository...
Initializing new repository...
Repository successfully created.

$ gitbox clone myapp
Cloning repository...
Repository successfully cloned.

After which normal git usage:

$ echo “Some change” > somefile.txt
$ git add somefile.txt
$ git commit –m “Created some file”
$ git push

Check the project wiki and the manuals for full command reference and tutorials.


It is now 2015, and as of three days ago, a new tool based on Dropbox API v2 has been created to safely use git on Dropbox. It works against the API rather than using the desktop client, and correctly handles multiple simultaneous pushes to a repository hosted in a shared folder.

Once configured, it allows one to set up a git remote exactly like any other git remote.

git clone "dropbox::/path/to/repo"
git remote add origin "dropbox::/path/to/repo"

I love the answer by Dan McNevin! I'm using Git and Dropbox together too now, and I'm using several aliases in my .bash_profile so my workflow looks like this:

~/project $ git init
~/project $ git add .
~/project $ gcam "first commit"
~/project $ git-dropbox

These are my aliases:

alias gcam='git commit -a -m'
alias gpom='git push origin master'
alias gra='git remote add origin'
alias git-dropbox='TMPGP=~/Dropbox/git/$(pwd | awk -F/ '\''{print $NF}'\'').git;mkdir -p $TMPGP && (cd $TMPGP; git init --bare) && gra $TMPGP && gpom'

I use Mercurial (or Git) + TrueCrypt + Dropbox for encrypted remote backups.

The coolest thing is that Dropbox does NOT sync the entire TrueCrypt container if you modify a small portion of your code. The sync time is roughly proportional to the amount of changes. Even though it's encrypted, the combination of TrueCrypt + Dropbox makes excellent usage of block cipher + block level sync.

Secondly, a monolithic encrypted container not just adds security, it also reduces chances of repository corruption .

Caution: However you have to be very careful about not having the container mounted while Dropbox is running. It can also be a pain to resolve conflicts if 2 different clients check-in different versions to the container. So, it's practical only for a single person using it for backups, not for a team.

Setup:

  • Create a Truecrypt container (multiple Gigabyte is fine)
  • Under Truecrypt preferences, uncheck preserve modification timestamp*.
  • Create a repo as mentioned above by Dan ( https://stackoverflow.com/a/1961515/781695 )

Usage:

  • Quit Dropbox
  • Mount the container, push your changes, unmount
  • Run dropbox

P.S. Unchecking the preserve modification timestamp tells dropbox that the file has been modified and it should be sync'd. Note that mounting the container modifies the timestamp even if you don't change any file in it. If you don't want that to happen, simply mount the volume as read-only


On MacOS you may also just stop Dropbox, make your changes and then relaunch Dropbox. I am using the following combination and am quite happy with it:

In both (your local git managed project directory and your remote git repository located on Dropbox) run the following command to disable auto-packing (which is the main problem with dropbox syncing)

git config --global gc.auto 0

Then from time to time, compress the repositories with dropbox disabled. For example I do the following in my bash-build-script whenever I make new releases of my apps.

osascript -e "tell application \"Dropbox\" to quit"

# Compress local
git gc --prune=now; git repack -a -d

# Compress remote
REPOS_DIR_REMOTE=`git remote get-url --push origin`
cd "${REPOS_DIR_REMOTE}"
git gc --prune=now; git repack -a -d

osascript -e "tell application \"Dropbox\" to launch"
osascript -e "display notification with title \"Compress Done\""

I didn't want to put all my projects under one Git repository, nor did I want to go in and run this code for every single project, so I made a Bash script that will automate the process. You can use it on one or multiple directories - so it can do the code in this post for you or it can do it on multiple projects at once.

#!/bin/sh
# Script by Eli Delventhal
# Creates Git projects for file folders by making the origin Dropbox. You will need to install Dropbox for this to work.

# Not enough parameters, show help.
if [ $# -lt 1 ] ; then

cat<<HELP
projects_to_git.sh -- Takes a project folder and creates a Git repository for it on Dropbox

USAGE:
    ./projects_to_git.sh file1 file2 ..

EXAMPLES:
    ./projects_to_git.sh path/to/MyProjectDir
        Creates a git project called MyProjectDir on Dropbox

    ./projects_to_git.sh path/to/workspace/*
        Creates a git project on Dropbox for every folder contained within the workspace directory, where the project name matches the folder name

HELP
    exit 0
fi

# We have enough parameters, so let's actually do this thing.

START_DIR=$(pwd)

# Make sure we have a connection to Dropbox
cd ~
if [ -s 'Dropbox' ] ; then
    echo "Found Dropbox directory."
    cd Dropbox
    if [ -s 'git' ] ; then
        echo "    Dropbox Git directory found."
    else
        echo "    Dropbox Git directory created."
        mkdir git
    fi
else
    echo "You do not have a Dropbox folder at ~/Dropbox! Install Dropbox. Aborting..."
    exit 0
fi

# Process all directories matching the passed parameters.
echo "Starting processing for all files..."
for PROJ in $*
do
    if [ -d $PROJ ] ; then
        PROJNAME=$(basename $PROJ)
        echo "  Processing $PROJNAME..."

        # Enable Git with this project.
        cd $PROJ
        if [ -s '.git' ] ; then
            echo "    $PROJNAME is already a Git repository, ignoring..."
        else
            echo "    Initializing Git for $PROJNAME..."
            git init -q
            git add .
            git commit -m "Initial creation of project." -q

            # Make the origin Dropbox.

            cd ~/Dropbox/git
            if [ -s $PROJNAME ] ; then
                echo "    Warning! $PROJNAME already exists in Git! Ignoring..."
            else
                echo "    Putting $PROJNAME project on Dropbox..."
                mkdir $PROJNAME
                cd $PROJNAME
                git init -q --bare
            fi

            # Link the project to the origin
            echo "    Copying local $PROJNAME to Dropbox..."
            cd $PROJ
            git remote add origin "~/Dropbox/git/$PROJNAME"
            git push -q origin master
            git branch --set-upstream master origin/master
        fi
    fi
done

echo "Done processing all files."
cd $START_DIR

With regards to small teams using Dropbox:

If each developer has their own writable bare repository on Dropbox, which is pull only to other developers, then this facilitates code sharing with no risk of corruption!

Then if you want a centralized 'mainline', you can have one developer manage all the pushes to it from their own repo.


For my 2 cents Dropbox only makes sence for personal use where you don't want to bother getting a central repo host. For any professional development you'll probably create more problems than you'll solve, as have been mentioned several times in the thread already, Dropbox isn't designed for this use case. That said, a perfectly safe method to dump repositories on Dropbox without any third-party plugins or tools is to use bundles. I have the following aliases in my .gitconfig to save typing:

[alias]
        bundle-push = "!cd \"${GIT_PREFIX:-.}\" && if path=\"$(git config remote.\"$1\".url)\" && [ \"${path:0:1}\" = / ]; then git bundle create \"$path\" --all && git fetch \"$1\"; else echo \"Not a bundle remote\"; exit 1; fi #"
        bundle-fetch = "!cd \"${GIT_PREFIX:-.}\" && if path=\"$(git config remote.\"$1\".url)\" && [ \"${path:0:1}\" = / ]; then git bundle verify \"$path\" && git fetch \"$1\"; else echo \"Not a bundle remote\"; exit 1; fi #"
        bundle-new = "!cd \"${GIT_PREFIX:-.}\" && if [ -z \"${1:-}\" -o -z \"${2:-}\" ]; then echo \"Usage: git bundle-new <file> <remote name>\"; exit 1; elif [ -e \"$2\" ]; then echo \"File exist\"; exit 1; else git bundle create \"$2\" --all && git remote add -f \"$1\" \"$(realpath \"$2\")\"; fi #"

Example:

# Create bundle remote (in local repo)
$ git bundle-new dropbox ~/Dropbox/my-repo.bundle
# Fetch updates from dropbox
$ git bundle-fetch dropbox
# NOTE: writes over previous bundle. Thus, roughly equivalent to push --force --prune --all
$ git bundle-push

This answer is based on Mercurial experience, not Git, but this experience says using Dropbox this way is asking for corrupt repositories if there's even a chance that you'll be updating the same Dropbox-based repository from different machines at various times (Mac, Unix, Windows in my case).

I don't have a complete list of the things that can go wrong, but here's a specific example that bit me. Each machine has its own notion of line-ending characters and how upper/lower case characters are handled in file names. Dropbox and Git/Mercurial handle this slightly differently (I don't recall the exact differences). If Dropbox updates the repository behind Git/Mercurial's back, presto, broken repository. This happens immediately and invisibly, so you don't even know your repository is broken until you try to recover something from it.

After digging out from one mess doing things this way, I've been using the following recipe with great success and no sign of problems. Simply move your repository out of Dropbox. Use Dropbox for everything else; documentation, JAR files, anything you please. And use GitHub (Git) or Bitbucket (Mercurial) to manage the repository itself. Both are free so this adds nothing to the costs, and each tool now plays to its strengths.

Running Git/Mercurial on top of Dropbox adds nothing except risk. Don't do it.


We use this method (creating a bare repository in Dropbox) on a share folder.

A small group of developers can pull from that bare synced repository and create a local clone. Once the unit of work is done, we push back to origin.

One thing I'm missing is a good way to have an e-mail sent with the change-set information once a push to origin occurs. We are using Google Wave to manually keep track of changes.


Another approach:

All the answers so far, including @Dan answer which is the most popular, address the idea of using Dropbox to centralize a shared repository instead of using a service focused on git like github, bitbucket, etc.

But, as the original question does not specify what using "Git and Dropbox together effectively" really means, let's work on another approach: "Using Dropbox to sync only the worktree."

The how-to has these steps:

  1. inside the project directory, one creates an empty .git directory (e.g. mkdir -p myproject/.git)

  2. un-sync the .git directory in Dropbox. If using the Dropbox App: go to Preferences, Sync, and "choose folders to sync", where the .git directory needs to get unmarked. This will remove the .git directory.

  3. run git init in the project directory

It also works if the .git already exists, then only do the step 2. Dropbox will keep a copy of the git files in the website though.

Step 2 will cause Dropbox not to sync the git system structure, which is the desired outcome for this approach.

Why one would use this approach?

  • The not-yet-pushed changes will have a Dropbox backup, and they would be synced across devices.

  • In case Dropbox screws something up when syncing between devices, git status and git diff will be handy to sort things out.

  • It saves space in the Dropbox account (the whole history will not be stored there)

  • It avoids the concerns raised by @dubek and @Ates in the comments on @Dan's answer, and the concerns by @clu in another answer.

The existence of a remote somewhere else (github, etc.) will work fine with this approach.

Working on different branches brings some issues, that need to be taken care of:

  • One potential problem is having Dropbox (unnecessarily?) syncing potentially many files when one checks out different branches.

  • If two or more Dropbox synced devices have different branches checked out, non committed changes to both devices can be lost,

One way around these issues is to use git worktree to keep branch checkouts in separate directories.


I have faced a similar issue and have created a small script for the same. The idea is to use Dropbox with Git as simply as possible. Currently, I have quickly implemented Ruby code, and I will soon add more.

The script is accessible at https://github.com/nuttylabs/box-git.


Without using third-party integration tools, I could enhance the condition a bit and use DropBox and other similar cloud disk services such as SpiderOak with Git.

The goal is to avoid the synchronization in the middle of these files modifications, as it can upload a partial state and will then download it back, completely corrupting your git state.

To avoid this issue, I did:

  1. Bundle my git index in one file using git bundle create my_repo.git --all.
  2. Set a delay for the file monitoring, eg 5 minutes, instead of instantaneous. This reduces the chances DropBox synchronizes a partial state in the middle of a change. It also helps greatly when modifying files on the cloud disk on-the-fly (such as with instantaneous saving note-taking apps).

It's not perfect as there is no guarantee it won't mess up the git state again, but it helps and for the moment I did not get any issue.


I've been using Mercurial in the recommended manner and urge that you be cautious, especially if any of the machines differ. The Dropbox fora are full of complaints of mysterious filename case problems turning up spontaneously. Hg (and I presume Git) won't notice or complain during routine checkins and you'll only hear about the corruption when it complains of a corrupt repo when you try to use it for real. Bad news. Wish I could be more specific about the problem and its workarounds; I'm still trying to dig out from this mess myself.


I like the top-voted answer by Dan McNevin. I ended up doing the sequence of git commands too many times and decided to make a script. So here it is:

#!/bin/bash

# Usage
usage() {
    echo "Usage: ${0} -m [ master-branch-directory ] -r [ remote-branch-directory ] [ project-name ]"
    exit 1
}

# Defaults
defaults() {
    masterdir="${HOME}/Dropbox/git"
    remotedir="${PWD}"
    gitignorefile="# OS generated files #\n\n.DS_Store\n.DS_Store?\n.Spotlight-V100\n.Trashes\nehthumbs.db\nThumbs.db"
}

# Check if no arguments
if [ ${#} -eq 0 ] ; then
    echo "Error: No arguments specified"
    usage
fi

#Set defaults
defaults

# Parse arguments
while [ ${#} -ge 1 ]; do
    case "${1}" in
        '-h' | '--help' ) usage ;;
        '-m' )
            shift
            masterdir="${1}"
            ;;
        '-r' )
            shift
            remotedir="${1}"
            ;;
        * )
            projectname="${1##*/}"
            projectname="${projectname%.git}.git"
            ;;
    esac
    shift
done

# check if specified directories and project name exists
if [ -z "${projectname}" ]; then
    echo "Error: Project name not specified"
    usage
fi

if [ ! -d "${remotedir}" ]; then
    echo "Error: Remote directory ${remotedir} does not exist"
    usage
fi

if [ ! -d "${masterdir}" ]; then
    echo "Error: Master directory ${masterdir} does not exist"
    usage
fi

#absolute paths
remotedir="`( cd \"${remotedir}\" && pwd )`"
masterdir="`( cd \"${masterdir}\" && pwd )`"

#Make master git repository
cd "${masterdir}"
git init --bare "${projectname}"

#make local repository and push to master
cd "${remotedir}"
echo -e "${gitignorefile}" > .gitignore # default .gitignore file
git init
git add .
git commit -m "first commit"
git remote add origin "${masterdir}/${projectname}"
git push -u origin master

#done
echo "----- Locations -----"
echo "Remote branch location: ${remotedir}"
echo "Master branch location: ${masterdir}"
echo "Project Name: ${projectname}"

The script only requires a project name. It will generate a git repository in ~/Dropbox/git/ under the specified name and will push the entire contents of the current directory to the newly created origin master branch. If more than one project name is given, the right-most project name argument will be used.

Optionally, the -r command argument specifies the remote branch that will push to the origin master. The location of the project origin master can also be specified with the -m argument. A default .gitignore file is also placed in the remote branch directory. The directory and .gitignore file defaults are specified in the script.


I store my non-Github repo's on Dropbox. One caveat I ran into was syncing after a reinstall. Dropbox will download the smallest files first before moving to the larger ones. Not an issue if you start at night and come back after the weekend :-)

My thread - http://forums.dropbox.com/topic.php?id=29984&replies=6


The right way to do this is use git-remote-dropbox: https://github.com/anishathalye/git-remote-dropbox

Creating your own bare repo in Dropbox causes a lot of problems. Anish (the creator of the library) explains it best:

The root cause of these problems is that the Dropbox desktop client is designed for syncing files, not Git repositories. Without special handling for Git repositories, it doesn’t maintain the same guarantees as Git. Operations on the remote repository are no longer atomic, and concurrent operations or unlucky timing with synchronization can result in a corrupted repository.

Traditional Git remotes run code on the server side to make this work properly, but we can’t do that.

Solution: It is possible to solve this properly. It is possible to use Git with Dropbox and have the same safety and consistency guarantees as a traditional Git remote, even when there are multiple users and concurrent operations!

For a user, it’s as simple as using git-remote-dropbox, a Git remote helper that acts as a transparent bidirectional bridge between Git and Dropbox and maintains all the guarantees of a traditional Git remote. It’s even safe to use with shared folders, so it can be used for collaboration (yay unlimited private repos with unlimited collaborators!).

With the remote helper, it’s possible to use Dropbox as a Git remote and continue using all the regular Git commands like git clone, git pull, and git push, and everything will just work as expected.


I don't think that using Git and Dropbox is the way to go... Just think about the features of both:

Git:

  • Allows you to have a central repository
  • Allows you to have your own repository with your own changes
  • Allows you to send and receive changes from the central repository
  • Allows multiple persons to change the same files and them merges them or asks you to merge them if it can't do it
  • Has web and desktop clients to allow access to the central repository

Dropbox:

  • Keeps everything in a central repository
  • Allows you to have your own versions of the files in the server
  • Forces you to send and receive changes from the central repository
  • If multiple persons change the same files, the first file committed is replaced with later commits, and no merge occurs which is troublesome (and definitely its biggest disadvantage)
  • Has web and desktop clients to allow access to the central repository.

And if you're worried with sharing some of your files, why not cipher them? And then you could get the biggest advantage of Dropbox to Git, that is, to have public and private files...