Today we have an example of a massive disaster caused by push -f. Much of the discussion is less than productive, because it violates a fundamental tenet of user interaction design: You are not allowed to call the user stupid. If the user did something wrong, the tool either steered them in the direction of doing something they shouldn’t have, or didn’t allow them to undo something they wanted to undo.
In the search for ultimate rather than proximate solutions to problems like this, it’s important to remember that the software is not immovable. All aspects of how a tool works, in this case both the underlying functionality of git and the process flow on github, are in principle subject to change. Obviously reinventing the tool from scratch is a bad idea: the solution should require as little new development as possible, and ideally shouldn’t break anything which already exists, but all approaches should be considered.
Almost all the discussion of solutions is focusing on a feature request to github to allow admins to disallow push -f. That’s a reasonable enough approach, but it’s being emphasized because it’s the approach which is made possible by the local administration tools available today, not because it’s the best approach. Git’s push -f is a very dangerous command whose use is encouraged by standard git methodologies, but it’s also a very powerful and useful tool, so blocking its use comes with significant downsides.
There are means by which push -f could be made much less dangerous, and more convenient and useful, but that’s an involved discussion I’ll save for another day. What I’d like encourage as the most immediate solution to this sort of problem is the alternate approach, where the user is given the ability to undo a mistake.
If you’re running git locally, there already is a facility for doing this, albeit not a very good one: The reflog command. What there should be is a remote (and better) version of the reflog command, which gives access to the history of every change to head which has been made, including when it was done and which user did it. Naturally this should be paired with another command for obliterating the history of past heads, because there are times when it makes sense to remove all trace of something which happened (for example, when someone added a password file) but completely obliterating history shouldn’t be the default, and it certainly shouldn’t be conflated with the functionality which push -f is often used for. The details of how reflog works feel accidental rather than designed – it doesn’t keep everything, nor does it allow you to immediately completely obliterate old things. Such functionality is important enough that it deserves to be designed on purpose and put into the standard toolset.
(A funny aside, I’m told the command is pronounced ref-log, short for ‘reference log’. I always assumed it was ‘re-flog’ because looking at old history is beating a dead horse.)