You can literally set up the "exchange / backup" remote as a mirror (a bare clone that takes "force" style ref updates, rather than requiring that some or all ref updates be fast-forwards).
"Normal" clones are set up to do force-updates of refs/remotes/*
: take a look at .git/config
(or use git config --get remote.origin.fetch
) and you'll see that the fetch refspec for origin
is:
+refs/heads/*:refs/remotes/origin/*
The plus sign means "force", i.e., "don't require a fast forward". Hence, when you git fetch origin
, whatever commit SHA1 that (e.g.) origin's refs/heads/master
names, becomes your local refs/remotes/origin/master
, no matter what commit SHA1 your refs/remotes/origin/master
used to point-to. This is generally safe (and hence the default even for "ordinary" clones) because these "remote branch" SHA1-pointer names wind up in refs/remotes/
rather than refs/heads
, i.e., it does not affect your local repo's branch name-space in any way.
When you use git push -f
—or specify a push refspec starting with a +
, which means the same for push as for fetch—this tells the remote that it should also allow non-fast-forwards. (That is, it tells the—for want of a better phrase—"built in hook" to allow fast forwards. Other git hooks on the remote to which you are pushing can still reject the change.) This is generally not safe because it affects the remote repo's name-space: you're updating its refs/heads/master
, not something like refs/pushes/JohnsMachineAtHome/master
. But if you know what you're doing, and don't make mistakes (or at least not often :-) ), it's OK after all.
Remember also that every commit in git is reasonably sticky as long as there is some reference (e.g., the reflog) pointing to it, so in that particular sense it's safe to force-push and force-fetch as long as you commit first. (As in, if you goof up, you might have to ssh in to work/home from home/work and poke around in the reflog to find a commit you dropped on your mirror.) Be aware that bare clones (including bare mirror clones) don't keep all that much in the way of reflogs (there is some data kept, especially with core.logAllRefUpdates
set; see git config docs) since the updates they get are en-masse at push or fetch time.
See also these git config options:
remote.<name>.push
The default set of "refspec" for git-push(1). See git-push(1).
remote.<name>.mirror
If true, pushing to this remote will automatically behave as if the
--mirror option was given on the command line.