Can I store the .git folder outside the files I want tracked?

Viewed 55964

I have an unusual idea to use git as a backup system. So let's say I have a directory ./backup/myfiles and I want to back that up using git. To keep things clean I don't want to have a .git directory in the myfiles folder, so I thought I could create ./backup/git_repos/myfiles. From looking at the git docs, I've tried doing this:

$ cd backup/myfiles
$ mkdir ../git_repos/myfiles
$ git --git-dir=../git_repos/myfiles init
Initialized empty Git repository in backup/git_repos/myfiles/
$ git --git-dir="../git_repos/myfiles/" add foo
fatal: pathspec 'foo' did not match any files

You can see the error message I get there. What am I doing wrong?

10 Answers
git --git-dir=../repo --work-tree=. add foo

This will do what you want but will obviously suck when you have to specify it with every git command you ever use.

You can export GIT_WORK_TREE=. and GIT_DIR=../backup and Git will pick them up on each command. That will only comfortably allow you to work in a single repository per shell, though.

I’d rather suggest symlinking the .git directory to somewhere else, or creating a symlink to the .git directory from your main backup directory.

It's conventional to name a directory that is a git repository that has its working tree in an unusual place with a '.git' extension, much like a bare repository.

mkdir ../git_repos/myfiles.git

If you had provided the --work-tree option at init time then this would have automatically set up the core.worktree config variable that means that git will know where to find the working tree once you specify the git directory.

git --git-dir=../git_repos/myfiles.git --work-tree=. init

But you can set this variable after the fact as well.

git --git-dir=../git_repos/myfiles.git config core.worktree "$(pwd)"

Once you've done this, the add command should work as expected.

git --git-dir=../git_repos/myfiles.git add foo

You could create a "nodgit" script (No Dot GIT) with somet like

#!/bin/sh
gits=/usr/local/gits
    x=`pwd`
    testdir() {( cd $1; pwd; )}
    while [ "$x" != "/" ]; do
      y=`echo $x|sed -e "s/\//__/g"`
      if ([ -d "$gits/$y" ]); then
        export GIT_DIR="$gits/$y"
        export GIT_WORK_TREE="$x"
        if ([ "$1" = "nodinit" ]); then
          mkdir -p "$GIT_DIR"
          git init --bare; exit $?
        elif ([ "$1" = "shell" ]); then
          bash; exit $?
        else
          exec git "$@"
        fi
      fi
      x=`testdir "$x/.."`
    done

You can call nodgit in place of git and it will set variables as necessary by looking for a git repo. For example say you have a (bare) repo in /usr/local/gits/__home__foo_wibbles and you are in in /home/foo/wibbles/one then it will find the correct working directory (/home/foo/wibbles) and repo.

Oh you can also use "nodgit shell" to get a shell with the correct vars set so you can use plain old git commands.

Assuming your myfiles directories already exists and has some content, could you live with this:

cd ~/backup
git init
git add myfiles

The .git directory will be in backup, not in myfiles.

To clarify the difference between the two options below, you can tell your git repository to track files somewhere else (core.worktree) or you can put a breadcrumb in your main worktree that points to a git repository folder somewhere else (--separate-git-dir). The core.worktree option does not need a breadcrumb file and has been a feature of git since first release. The second option is newer and requires a breadcrumb file, but doesn't require altering the repository configuration file.

Option 1: core.worktree

Initialize a non-bare repository outside of the path you want to track and set core.worktree to the path you want to track. You can do this using terminal commands to set this value or directly edit the repository config file to add:

worktree = <path to files to backup>

Do not make the repository folder a subfolder of this path; that would be recursive. You could possibly try this and simply ignore the repository folder, but I think git won't allow this scenario.

In your case, you would go to backup/git_repos/ to run the init command and could use the --git-dir=./myfiles option to override the default repository folder name. The commands would look like this:

cd backup/git_repos 
git init --git-dir=./myfiles
git config core.worktree backup/myfiles

NOTE 1: This is not the same as the newer ADDITIONAL worktree functionality of git. This is your main working tree which is used by all non-bare repositories. Those additional worktrees use breadcrumbs to subfolders of the git repository folder, much like Option 2 below.

NOTE 2: I recently tested a great many git GUIs for windows and only Git Extensions supports using core.worktree to move the main working tree.

GUIs that do not support core.worktree

SourceTree, Fork, Tower, GitKraken, GitHub Desktop, GitAhead, SmartGit*, and Git-Cola. You will want to stick to the terminal when using core.worktree.

* SmartGit conflaits this feature with Option 2 and is asking for a .git file. This is not required for core.worktree.

Option 2: --separate-git-dir

Initialize a repository at the path you want to backup using --separate-git-dir=<path to hold repository data>. This will use the specified path to hold the repository data and create a .git file in the initialization location that contains a line like this:

gitdir: <path to hold repository data>

For you, the commands would look like this:

cd backup/myfiles
git init --separate-git-dir=backup/git_repos/myfiles/

And your .git file in backup/myfiles/ will contain gitdir: backup/git_repos/myfiles/

You now operate git treating the location of the .git file as if that was the repository location.

Related