Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

--force-with-lease can be a footgun.

It will overwrite the tree on remote as long as remote hasn't changed since you last fetched it. It doesn't always work, particularly if you have a tool which continuously fetches remote, like an IDE configured to do so such as VSCode. In that case, you will have fetched the other person's changes, and --force-with-lease will happily blow-away anything on remote that might not be in your tree yet.



Yes, but it's still better than --force under most circumstances.


The implication with anything with '--force' in it, is you shouldn't be doing it without talking to someone first.

Absolutely does not belong in automatic anything, anywhere.


Oh, obviously you need to know what you are doing.

I mostly use --force-with-lease to push something to my own branches. No need to talk to anyone.

Force pushing to other people's branches without asking is just rude.


Why use force at all, on a default command?


my flow is (on my-branch with no one else's commits)

* push some commits up to my remote branch

* git fetch

* git rebase master/main to get the latest stuff

* add changes on my-branch that use new stuff from master/main

* git push --force-with-lease to my remote branch - this fails if you don't use some version of force since my most recent commit is based on a commit (from master) not on the remote branch


I don’t get why everybody wants to rebase their topic branches. Just use merge, come on. If you want a „clean“ commit history on main/master do a squashed merge into main at the end.

This way we never had to force anything on the remote.


I rebase on my topic branches because then my edits are neatly stacked on top of the other branch, so I can re-arrange things more easily. Why would I want a weird commit with a bunch of work I didn't do on the topic just smooshed into the middle of my well-crafted series of commits?


Work you didn't do won't be in your branch. There is no rearranging, it is one commit.


> Work you didn't do won't be in your branch.

The merge commit will be in the branch.

> There is no rearranging, it is one commit.

You misread that. They want to be able to rearrange things easily. Having multiple merge commits in the middle gets in the way of that.


The work doesn't show up on a diff, what I was getting at. Merges are from master in this example and already reconciled.

Commits don't matter either, because they are being squashed. Was responding to the grandparent perhaps more than the parent.


Why would you have to force anything with rebase? you rebase your feature branch against main to rewind it on top of it and clean up history so you can do a clean fast forward merge. Squashing is bad for anything non trivial, you want small independent commits: easy to review, easy to revert, easy to blame if something goes wrong.


As soon as you pushed your branch to remote (which I tend to do for backup reasons especially after working hard on a solution) rebase only means trouble.


Not if it's the remote for your own dev branch. It only matters if the remote branch is being (actively) used by other people.

Unfortunately I've met far too many who have your "remote" superstition. I remember arguing this exact point in my last gig, when someone was mad at me for force pushing my own remote branch that nobody else was using nor should have been.


if you're the only one working on that branch I don't see where is the problem in rewriting history and force pushing


Until you make a mistake.


Why? That's what 'git reflog' is for.

Or you mean that a mistake where you accidentally push to someone else's branch?

The default model that public github uses is good for that: everyone works on their own fork of the repo, and makes pull requests to the shared repo. Nobody pushes directly to the shared repo.


Second. Github not an option for a lot/most work.


From https://git-scm.com/docs/git-push

A general note on safety: supplying this option without an expected value, i.e. as --force-with-lease or --force-with-lease=<refname> interacts very badly with anything that implicitly runs git fetch on the remote to be pushed to in the background, e.g. git fetch origin on your repository in a cronjob.

The protection it offers over --force is ensuring that subsequent changes your work wasn’t based on aren’t clobbered, but this is trivially defeated if some background process is updating refs in the background. We don’t have anything except the remote tracking info to go by as a heuristic for refs you’re expected to have seen & are willing to clobber.

If your editor or some other system is running git fetch in the background for you a way to mitigate this is to simply set up another remote:

    git remote add origin-push $(git config remote.origin.url)
    git fetch origin-push
Now when the background process runs git fetch origin the references on origin-push won’t be updated, and thus commands like:

   git push --force-with-lease origin-push
Will fail unless you manually run git fetch origin-push. This method is of course entirely defeated by something that runs git fetch --all, in that case you’d need to either disable it or do something more tedious like:

    git fetch              # update 'master' from remote
    git tag base master    # mark our base point
    git rebase -i master   # rewrite some commits
    git push --force-with-lease=master:base master:master
I.e. create a base tag for versions of the upstream code that you’ve seen and are willing to overwrite, then rewrite history, and finally force push changes to master if the remote version is still at base, regardless of what your local remotes/origin/master has been updated to in the background.

Alternatively, specifying --force-if-includes as an ancillary option along with --force-with-lease[=<refname>] (i.e., without saying what exact commit the ref on the remote side must be pointing at, or which refs on the remote side are being protected) at the time of "push" will verify if updates from the remote-tracking refs that may have been implicitly updated in the background are integrated locally before allowing a forced update.


The existence of such a complicated workaround mainly serves as an official confirmation that you should just turn off auto fetch or not use force-with-lease. I use PyCharm's Update Project to fetch all branches at once on demand, so I will always get anyone's changes to my branch at the same time I fetch.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: