Although the responsible developer's reaction and attitude are both commendable, one element of his response annoyed me: his continued assertion that he should not have been allowed to do this thing that he did.
I think it is a truism that systems which allow users to do interesting and clever things must also allow users to do remarkably stupid and wrong things.
Rather than focusing on how to prevent a user from doing a silly thing like this, I think a well-designed tool would easily allow the user to undo the silly thing he just did.
Unfortunately, all too often when a Bad Thing happens, the discussion tends to center around how to prevent this Bad Thing from happening again. This is often why those who make mistakes end up being demonized; their mistake has now limited the actions that every other user of the system can make, because the system will be modified to prevent other users from making the same mistake.
> I think a well-designed tool would easily allow the user to undo the silly thing he just did.
Agreed. From a usability perspective, undo is always best. Especially undo that the user: 1) knows about, 2) trusts, and 3) is fast. That encourages exploration and lets users fix mistakes.
> their mistake has now limited the actions that every other user of the system can make, because the system will be modified to prevent other users from making the same mistake.
I don't think this is a black and white issue. It's not about denying all users the ability to express X ever again. I think it's more about having systems that can tell if X is an unusual or heavyweight action and say, "Hey, you're about to do X, which impacts a lot of stuff and you've never done before. Are you sure that's what you mean?"
I don't think this is a black and white issue. It's not about denying all users the ability to express X ever again. I think it's more about having systems that can tell if X is an unusual or heavyweight action and say, "Hey, you're about to do X, which impacts a lot of stuff and you've never done before. Are you sure that's what you mean?"
Yes, but git already does that, by having the user type "--force". Asking would eliminate the possibility of scripting the action.
how far does that go though? `git push --force --yes --yes-I-am-absolutely-sure --by-pushing-this-commit-I-agree-I-am-paying-attention`. Seems like `--force` is a succinct way to cover those two parts.
that's why a have a easy to type password to unlock my local private key, and i set my keymanager to never cache the password. It is always nice to have that last chance to review your changes to the VCS.
...not that it would have helped in this case as he was probably using it heedlessly in a jenkins plugin.
From my reading of his post, the one big failure of the system is that he had an access he didn't think he had. This is sort of like me typing find . | xargs -n0 rm -rf in the root directory and thinking "it's OK because it will only delete files I have permission to delete" but not realizing that I'm root.
I could certainly see the argument that the system should have made him more aware of the permissions he had. But it seems like instead he's arguing that he shouldn't have been given those permissions to begin with, which goes back to my point. Because one person was given many permissions and used them irresponsibly, now everyone's default permissions are more restrictive.
More systems need to implement undo (and scalable undo). Hiding behind "are you sure? (Y/N)" type of prompts and saying "Well, you really should've checked that thrice before hitting Enter" is not good enough.
Git provides all the pieces, we just need to put them together in the right way.
The issue is that a client may not have fetched for a while before performing the force push, so it may not have all the required refs for performing the rollback.
The server of course still has all the refs, so it shouldn't be too hard to add a command (run client side) to invoke the required server-side rollback.
Being able to write custom update hooks also allows you to do a lot of neat things (in this case, an update hook could prevent non-fastforward pushes).
You can already do this with git config, but unfortunately it's a giant hammer that affects the entire repo at once.
It is of course possible to write a post-receive hook that denies non-fastforward pushes to specific branches, but really, git should include a config option out of the box to do this at a more granular level. A common workflow is the expectation that people should be able force-push to their own topic branches all they like, but never to the default branch (except in extreme circumstances). Being able to easily set this via a config option would be very helpful in keeping working repos more "safe" from this kind of thing.
Ideally personal topic branches, not yet ready for world consumption, would be pushed but to a different repo. This would allow the main repo to have a global "No force pushes!" setting, and would prevent branch name collisions (how many developers have a scratch branch sitting around named 'scratch'?)
That's fine for some projects, but not every project follows that flow. One example: ompanies using GitHub Enterprise internally generally don't make every regular contributor to a project have their own fork (or at least they shouldn't, as it creates needless extra overhead). In that case, having some extra granularity around denying non-fastforward pushes to some branches but not others would be a helpful addition.
I think it is a truism that systems which allow users to do interesting and clever things must also allow users to do remarkably stupid and wrong things.
Rather than focusing on how to prevent a user from doing a silly thing like this, I think a well-designed tool would easily allow the user to undo the silly thing he just did.
Isn't that exactly what a revision control system is for?
In the case of git 'rewriting history' is not really the problem, and it leaves the old history intact (you cannot change git history, but you can create an alternative history).
The old history will possibly be garbage collected eventually, but for quite a while that history is still accessible.
I think the only missing piece is a simple (and obvious) client side 'undo/rollback that force push'.
The issue is that the git client may not have the history pre-force, so it may have insufficient information to do the rollback (at least in the current model), however the server still has all the refs (and they are listed in the reflog).
Yes, the ability to rollback history more easily than paging through `git reflog` would be great. (in particular, if you rebase often you end up with a lot of cruft).
Locally in git you have the HEAD@{n} notation to step back n steps through the reflog, so after a bad rebase a `git reset --hard HEAD@{1}` should be all you need.
Right. So if your revision control system -- your failsafe in the event of unpredicted mistakes -- is configured such that you can lose data permanently, I think it is fair to ask whether anything can be changed to improve how that tool operates. This doesn't mean you can't also ask what else might be done to avoid any unfortunate repetition of the same problem situation, of course.
Nothing was or could have been lost permanently. The commits were all still there, it's just that none of them were in the commit history for any branches anymore. And the entire history including sufficient info to restore the state prior to the `push -f` is all there in the reflog.
It's just a pain in the butt to restore it all, especially if in the meantime people started making new commits on top.
But if someone has commit privileges to a repo, they have the ability to mess it up, I don't see any real way around that. The UI of the client side tool(s) that made it so easy to make this mistake can perhaps be blamed.
Yes, it seems counterintuitive that a version control system should ever let you lose previously committed data. "What's the point of version control, if there's a chance you won't be able to revert?" you might ask.
So at first it seems like we should establish the principle that you can't permanently delete committed data. (Unless you step outside the tools provided by the VCS and do something like hacking the contents of the .git directory.)
But sadly, real-world constraints get in the way of this principle.
Disk space is one such constraint. Sometimes, we need to bring a repo's disk footprint down to a reasonable size. This could happen if we committed a bunch of huge files that we no longer want, or if the repo is simply very old.
Another constraint is intellectual property, secrets, passwords, and such things. Sometimes you check in something that other people shouldn't see. Maybe it's an accident, or maybe you just don't realize the data is sensitive. You need a way to redact the contents of the repo, and that means deleting historical data.
What sort of workflow are you using when you need to use --force every day?
The company I work for has at least 50 projects all using Git and the only time we use --force is when we have to clean up a major mistake (eg a premature merge to release). I can count the number of times this has happened in the past 3 years on my hands.
It's used often in a team where you each have your own forks. It's much less risky in such a case, of course. Basically, I push to my fork, then later I pull-with-rebase from the mainline, then I push again to my fork. Oops, commit hashes have changed because of my pull-with-rebase. So I need to either push to a different branch or force push.
I'm not sure a "problem" exists here. Keeping your personal topic branch rebased on top of the main development branch is a perfectly reasonable workflow, and quite comparable to what you'd do with Mercurial patch queues.
And in a team where everybody's used to that, and force is only ever used on topic branches where that's the expected 'done thing', it really doesn't cause a problem.
(on teams that like rebase I tend to do this a lot)
Why would you do this, rather than pull into a different branch name, fix it there, and then merge it in? Is there an advantage to doing it by altering your own repo?
Yes, but you can pull into a new local branch, do the history edit there, and then push that new branch, can't you? Not that I'm sure it would be worth it.
If you're working in an organization that employs pre-merge code review, you may have to modify your topic branch several times before it's ready to be merged into the main development branch. Creating a new branch every time so that you can push without force seems pointless in this case and increases confusion for everyone involved in the process.
I'm pleased that the reaction in this thread is generally with understanding towards the developer. We've all had facepalm moments like this, and probably not as publicly.
But I don't think Bad Thing Prevention is necessarily to be avoided. Undo buttons are sweet, but not everything can be undone. An anecdote: During the first month of my first job in finance I accidentally deleted a few mappings in a database which put the entire company at risk to the tune of about $45million. Complete bone-head move, I was new to the game. It all turned out fine and I got off with a slap on the wrist, but my manager was flogged. When markets are involved, something like that has no undo button. Instead, we put some proper controls in place (tests, mostly) to make sure said mappings were always logically consistent. As far as I know, that brand of problem has not occurred in the 6 years since.
That said, sometimes Bad Thing Prevention is something that should have been implemented long ago, and was hitherto unrecognized. My canonical example is increased regulations and requirements for entry when an untrained student was seriously injured in a university machine shop; training is something that can be attained, just as "being the one at the top of the organization with force push access" is something that can be attained. There's no loss of abilities, just a more-optimal mapping of abilities to the people who know how to use them.
Given that Git is such an integral part of the workflow, I find unnacceptable the ability to push to the central repo for people that can't handle Git well. Git is distributed, if you're using it like it's SVN you're doing it wrong.
And yes, we all make mistakes. But by Git's nature, lo and behold, everybody has a copy.
> his continued assertion that he should not have been allowed to do this thing that he did.
Well, if the repositories were created with "git init --shared" then it wouldn't have been allowed.
I think it's a valid position to believe that this should be the default on github, although obviously in this case, there might be blame-shifting motivation to that position.
> Rather than focusing on how to prevent a user from doing a silly thing like this, I think a well-designed tool would easily allow the user to undo the silly thing he just did.
Git does make it easy for the user to undo this. It provides the reflog for that. It also provides information in the output of the push command. (However, github does not allow users to access the remote reflog.)
In any case, you can blame github, and you can blame the developer, but at least we should all be clear that you can't blame git.
Well I think one needs both permission control and easy undo options.
His insistence that he should not have had right access does come across as a lame excuse, but he has a point. e.g. Take the other poster in the thread who was surprised to find that others have access to his project beyond the half a dozen he knew about - that to me indicates that something isn't quite right permissions-wise.
There is a very simple undo, just force push the branch you just deleted? It's not like force pushing is some terrible destructive thing, it just puts the repo in a state people don't expect.
I think it is a truism that systems which allow users to do interesting and clever things must also allow users to do remarkably stupid and wrong things.
Rather than focusing on how to prevent a user from doing a silly thing like this, I think a well-designed tool would easily allow the user to undo the silly thing he just did.
Unfortunately, all too often when a Bad Thing happens, the discussion tends to center around how to prevent this Bad Thing from happening again. This is often why those who make mistakes end up being demonized; their mistake has now limited the actions that every other user of the system can make, because the system will be modified to prevent other users from making the same mistake.