Note: A “backup” copy on the same machine is not much of a backup (especially if it is on the same disk). To be reliable you really need to copy your data to one (or more!) different machines/media, preferably in different locations.
It sounds like your “backup” is a bare repository, but want it to be a non-bare repository (i.e. you want it to have its own working tree checked out).
The problem is that pushing to a non-bare repository is usually not a good idea. Effectively, such pushes are likely to update the branch that HEAD points to without updating the index or working tree. This can lead to very confusing situations in the receiving repository (e.g. added files shown with a deleted status, etc.). For this reason, Git versions 1.7.0 and later default to refusing to accept pushes to the currently checked out branch of non-bare repositories.
Note: When you push to a bare repository for backup purposes, all the files contained in the pushed commits are there, they are just not checked out (they are compressed and kept in Git the object store as “loose objects” and “pack files”). The pushed data represents a full copy of history of the content that you committed and pushed. You just can not access the content directly. Instead, you must clone it to a non-bare repository repository (and thus checkout a commit) or use git archive
to extract a set of files without an additional repository or full checkout.
I am not really convinced that you need something like what you describe.
If you need to examine an old snapshot of your repository (and you do not feel like doing it in your normal working repository), then you should just clone a temporary copy and checkout the desired, old commit. Local clones are cheap since they can hardlink the object store files instead of copying them. The historical commit graph is generally all you need to “go back in time”. While Git will let you rewrite the history graph at will, you are not really in much danger of making unrecoverable changes since it usually requires -f
/--force
switches and/or provides recovery mechanisms (reflogs, refs/original/, minimum age requirements before collecting unreferenced objects, etc.).
It is a good idea to have another repository (especially on another machine in another location) where you can push your commits for backup purposes (so that you can recover from (e.g.) rm -rf working_repo
), but a bare repository is usually entirely sufficient. When you need to recover, you just make a clone. When you want to examine some old snapshot without disturbing your normal working repository, you make a temporary clone somewhere. With good commit hygiene, git diff
, git log
(especially the -p
and -S
options), and git show
can often provide whatever “archeological” information you might want from old commits without needing to checkout anything at all (they even work in bare repositories).
However, if you are willing to accept the risk, you can do exactly what you want.
Git, like any other “sharp” tool, will let you risk “shooting yourself in the foot” (pardon the mixed metaphor).
Create and configure your backup repository and add it as a remote in the working repository.
# paths to the repositories
WORKING=/path/to/working
BACKUP=/path/to/backup
# name for the backup repository in the working repository
REMOTE=backup
! test -d "$BACKUP" || (echo "error: $BACKUP already exists"; exit 1) &&
git clone --origin working "$WORKING" "$BACKUP" &&
( cd "$BACKUP" &&
git config receive.denyCurrentBranch false &&
git remote rm working
) &&
( cd "$WORKING" &&
git remote add "$REMOTE" "$BACKUP" &&
git config remote."$REMOTE".push 'refs/heads/*:refs/heads/*'
)
In the backup repository, setup a post-receive
or post-update
hook that does a git reset --hard
. This will keep the index and working tree up to date with the currently checked out branch.
The Git FAQ “Why won't I see changes in the remote repo after "git push"?” points to an example post-update
script that can do this relatively safely (it saves a stash if the working tree or the index are dirty—this can still lose untracked files if newly added files with the same pathnames are pushed).
( H="$BACKUP"/.git/hooks/post-update &&
curl http://utsl.gen.nz/git/post-update >$H && chmod +x "$H" )
Push to the backup repository when you want to update it.
(cd "$WORKING" && git push "$REMOTE")
If you use a setup like this, you should absolutely avoid working in the backup working tree. Any commits you make there, any staged changes you leave there, and any untracked files you leave there are liable to be lost.