Hacker News new | ask | show | jobs
by zingermc 2046 days ago
I've definitely been the victim of my own git hubris.

Once, when leaving a job, I decided to copy all of my local WIP branches to the server.

I whipped out this fancy --mirror option I had just heard of:

  git push --mirror $remote
Surprise! All branches on the remote repo got wiped. My local refs replaced the refs on the remote.

Somehow I found the right commits floating around in the git ether. I was able to recreate the branches, but I had to recreate their names by reading the commit log.

Did I mention this happened on my way out the door from a job? In retrospect, I shouldn't have panicked so much -- there were daily backups -- but the sheer terror I felt has made me read the man pages really closely to this day.

4 comments

I have a horrible feeling your answer to this is going to stress me out, but does that work even without a force push?
Yes, --mirror implies a level of force. The man page contains this:

"...locally updated refs will be force updated on the remote end, and deleted refs will be removed from the remote end..."

That breaks a very simple rule that a VCS tools should adhere consistently. Do not make destructive changes unless explicitly told to. Since most other commands to not allow you to do this without -f, the same should apply here.
I'm not sure I see your point. The entire reason `git push --mirror` exists is to turn the remote end into a mirror of the local end. By definition that's a destructive operation. What would it do without a further --force?

If you just want to push all branches or tags you should use --all or --tags.

Because you normally cannot make remote destructive changes without the --force. Unless you know the commands that does that by default. That is bad UX.
Probably because removing branches from the remote doesn't make the commit history inconsistent with any copies made before the push. That seems to be the only case where git second-guesses what you tell it to do.
mistakes happen. it is a testament to the power of git that your command didn't cause permanent damage. and yes, it could have been worse.

but if you want a system that protects you from these mistakes by making mistakes impossible, then you get a system that is very rigit and actually harder to use.

git protects you from these mistakes by making it possible to recover.

in this particular instance though, there could probably be some extra help to recover. (some way restore the removed refs)

this definitely shouldn't happen if shared branches are properly protected against non fastforward pushes
...and if you didn't know that, you shouldn't be using git!

To be fair, one should always look up options you are unfamiliar with before using - and Stack Exchange is not an authoritative source.

--mirror

...Newly created local refs will be pushed to the remote end, locally updated refs will be force updated on the remote end, and deleted refs will be removed from the remote end... [my emphasis.]

On the other hand, if you are on a system where the behavior can be fundamentally altered by multiple configuration options, this will not necessarily help you much.

...This is the default if the configuration option remote.<remote>.mirror is set.

>To be fair, one should always look up options you are unfamiliar with before using - and Stack Exchange is not an authoritative source

https://xkcd.com/293/

I get it, but I'm not sure you should fault Git for that. Nothing like that has ever happened to me and I wouldn't use a command I don't know in a critical setting...
Any sane piece of software should warn you before doing a highly destructive operation.

Something like

  You are trying to remove commits from twelve branches.
  Are you sure that's what you want to do?  
  Include the --force option to go ahead with this change.
would be a whole lot better.

Even if it gives you the tools to undo the mistakes, it saves you time and stress if it keeps you from making the mistakes in the first place.