44

Although I love the git history rewrite feature, how does one go about ensuring history isn't rewritten.

We dont mind what a programmer does on their own machine, but we need to ensure that a version is not pushed to the server that changes history.

ie We need to guarantee that a particular version from the past really was that version. So this would include preventing someone going through and permanently removes a file from the history, or permanently alters a file throughout all history.

Jay
  • 19,649
  • 38
  • 121
  • 184

3 Answers3

42

If you can run:

 git config --system receive.denyNonFastforwards true

on the server, that should take care of rewriting history case being pushed to said server.
However that is for the all repo, not for a specifc file or group of files.

git config:

receive.denyNonFastForwards

If you rebase commits that you’ve already pushed and then try to push again, or otherwise try to push a commit to a remote branch that doesn’t contain the commit that the remote branch currently points to, you’ll be denied. This is generally good policy; but in the case of the rebase, you may determine that you know what you’re doing and can force-update the remote branch with a -f flag to your push command.

The other way you can do this is via server-side receive hooks, which I’ll cover in a bit. That approach lets you do more complex things like deny non-fast-forwards to a certain subset of users.


As ebneter (who knows the importance of a coherent repository -- see the answer about SVN to Git migrations [question now deleted, 10K+ users only]) comments:

You might want to also add receive.denyDeletes true because otherwise, someone can just delete the branch and then push their rewritten one as a new branch, effectively rewriting history.

git config:

One of the workarounds to the denyNonFastForwards policy is for the user to delete the branch and then push it back up with the new reference. In newer versions of Git (beginning with version 1.6.1), you can set receive.denyDeletes to true:

$ git config --system receive.denyDeletes true

This denies branch and tag deletion over a push across the board — no user can do it. To remove remote branches, you must remove the ref files from the server manually. There are also more interesting ways to do this on a per-user basis via ACLs, as you’ll learn at the end of this chapter.

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
3

If you aren't using a new enough git for receive.denyNonFastForwards, you can enforce the policy through {pre,post}-receive (among others) hooks on the server, which allows a little bit more granularity for specific branches and so forth.

Some good examples (including one prohibiting history rewrites) are used by the GNOME project to manage all of the repositories there:

https://git.gnome.org/browse/sysadmin-bin/tree/git

I'd look in particular at pre-receive-check-policy.

mmoya
  • 1,901
  • 1
  • 21
  • 30
Matt Enright
  • 7,245
  • 4
  • 33
  • 32
0

If you cannot/do not want to disable Git history rewrite completely but want to get notified about every change of this nature and be able to recover even years later, there are extensions to enterprise Git servers like Gerrit that will detect history rewrites and branch deletions, will back them up under a special ref so that they can be restored if needed and will not be pruned by garbage collection. Gerrit administrators can still remove selected commits if needed for legal reasons.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203