I just rebase and force push. I really don't get all the people that say avoid doing that. If you're the only one working on that branch, there's no risk.
If you're working with other people on the branch, and no one has unpushed changes, it's also fine. Everyone will just need to reset to the origin version of the branch to keep working.
If people do have unpushed changes and you force push, then they need to spend some time untangling it. Which is annoying, but it's not the end of the world. I don't get all the fear about it.
Don't rebase and force push master, that'll piss everyone off, sure. But doing it in branches is fine in most cases.
This is where `git pull --rebase` could save you all some time. You can even modify your git configs if this happens enough.
You just merge main in. Don't worry about extra commits that appear. PR tools like Github won't make people review them if you've done it all right, they can tell what a Merge Commit looks like (don't go deleting the auto-generated messages for merge commits unless you know what you're doing).
Once you merge main into your out-of-date branch, it'll be up to date. And when you merge your branch back into main after the PR, it will work out. This is the standard way you would work on a branch with a coworker, instead of working alone.
There's nothing wrong with merging main in.
If you absolutely MUST have a perfect history of your commits on top of THE CURRENT state of main, start a new branch, start a new PR, IMO. Just don't change public history.
I rebase PRs all the time, since they are my branches and my colleagues don't depend on them. If they do, I communicate with them so they know what I'm doing.
Things get very messy when you rebase a branch that multiple people branched off from, without them knowing you are going to rebase etc.
For people who love a clean, linear history, this can be frustrating, because it creates a new commit that doesn't add anything new, it just merges two branches. At my current project, PRs get merged very slowly, and after every PR gets merged, we merge master back into all the branches, which has lead to a ridiculous number of merge commits, and the guy who loves clean history won't stop complaining about it.
The problem is: if you rebase instead, you don't actually change the old commit, you make a new commit that contains the same content change as the old one. But it's a new commit, different from the one you already pushed to remote. So now you want to push your new rebased commit (as well as everything from master) to remote, and git says that remote has a commit that you don't have locally. So it wants to rebase or merge that remote commit onto your local rebased commit, despite the fact that they contain the same change! Now if you choose to rebase your local changes onto the remote commit, it rebases all the commits that you just merged from master, creating new commits for them. Then pushes them to remote. Then you still can't merge your PR, because it now still doesn't have the original (unrebased) commits from master, because you just rebased them. But you've duplicated a ton of commits. This is a mess.
There are 3 ways around this:
* Replace the first rebase with a merge, merging master into your local branch.
* Replace the second rebase with a merge, from when you pulled remote when you wanted to push, duplicating the one commit you meant to rebase.
* throw away the old PR and create a new one.
The first and third are the good options. The second is bad because it has a duplicate commit, but it's not nearly as bad as when you tried to rebase twice in a row.
Seriously, if stuff gets complicated, throw away your PR. Or avoid it getting complicated by always merging. If you want clean history, your only option is to throw away your PR every time you merge master back into your branch. It's either that or accept extra merge commits.
A dirty shortcut to creating a new PR is to force push. This will throw away the original commit on remote and overwrite it with your new rebased commits. This will work fine as long as nobody has checked out that branch in the mean time. If someone else has checked it out and pushes to your remote branch again, they may still reintroduce the old unrebased commit, and you'll still have that duplicate commit.
I usually just accept extra merge commits. Sometimes I force push, but I really try not to. You have to be sure nobody else is using that branch. In a complicated setup with lots of developers, automated tests and build servers, I'm reluctant to count on that.
I've yet to build a big feature that wasn't worked on by 4 or 5 people at the same time. Since they're different vertical slices, they're different branches (and tickets). But even then you might have a frontend and backend person collaborating on the same branch for a small slice of functionality.
This is also not a problem as even with one branch for two people who work with it. In git there are constantly more actual branches around than you "realize". E.g. here there's 3 branches going around and you just merge/rebase them. One branch is the one in the shared repo. The other two branches are the local branches of the same name on each of the two developer's machines. They need merging or rebasing just like you merge/rebase with `master` itself.
Depending on what you changed, you'll have weird looking history and conflicts to solve (or commits to skip). Which needs some head wrapping around but git itself so far has never been confused by my "reckless force pushing" on a shared branch. It helps if you don't have two people actually changing the same parts of the code though. Then you're in a world of hurt for conflict resolution but it's not the force pushing part that creates the hurt. That's just always bad.
Before you bring this back into `master` you'll want to squash all this into one commit and thus all the weird history ceases to exist.
Also, communication is key. If someone force pushes, tell the other person and help them resolve any rebasing/merging conflicts w/ your knowledge of the changes you made. If two people sit in opposite corners, force pushing "their world view" without tell each other, you definitely will be loosing code.