Can git rebase completely remove remote history?
As we consider moving from SVN to git at work, a coworker has raised the concern that a malicious or accident-prone developer could use git rebase
to delete remote history from our shared repo.
Edit: As pointed out in the answers, entire branches could also be deleted from the re开发者_如何学Gomote repo with git push origin :branch-name
.
Is this a realistic problem? If so, what approach can we take to prevent it?
I tend to agree with your coworker that there is a problem here because:
- no matter how well you trust your committers, there is always a possibility of human error
- more onerous review processes (e.g. Gerrit) are not always appropriate
- restoring from backups can be slow and a PITA
Have you considered the receive.denyNonFastForwards
and receive.denyDeletes
config parameters? AFAICT these are available in Git 1.6 onwards.
From Pro Git:
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.To disable the ability to force-update remote branches to non-fast-forward references, set
receive.denyNonFastForwards
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 the author mentions, this rule can also be enforced via a receive hook (which is described later in Pro Git).
These techniques should protect against accidental (or malicious) lost history in your shared repo.
The history can be messed up using rebase, but normally remote repo will not accept the change that modifies the history (unless you use git push --force), but even more, the developer with push permission can delete the branch completely (git push origin :branch-name). So the simple rules are:
Do not let push permission to developers you do not trust.
When the repo is shared, do not mess with a history, avoid using rebase on past commits. Use merge or cherry-pick instead if you need to add something from different branch, in which case the history will not be affected.
You may maintain the policy of not using 'push -f' on shared repo, in which case developer will know that if the push is rejected then something goes wrong (most likely local branch is not up to date with remote) and should resolve the problem locally rather than forcing the push.
Regarding your question how to prevent - use Gerrit revision system, it is like an intermediate step on the way of the commit between developer's local repository and master repo with nice web interface for revision, you may give permissions to push to revision repository to anyone, but the change will be merged into your master branch after verification and approval (which require some permissions you usually grant to core developers). You may see how it looks like on the Mahara project: https://reviews.mahara.org In this particular case, only gerrit bot is allowed to push to master (which is here) and noone else.
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.
精彩评论