536

I just squashed some commits with git rebase and did a git push --force (which is evil, I know).

Now the other software engineers have a different history and when they do a git pull, Git will merge. Is there a way to fix this, except doing a rm my-repo; git clone [email protected]:my-repo.git?

I need something like the opposite of git push --force, but git pull --force did not give the intended results.

3

5 Answers 5

771

To receive the new commits

git fetch

Reset

You can reset the commit for a local branch using git reset.

To change the commit of a local branch:

git reset origin/main --hard

Be careful though, as the documentation puts it:

Resets the index and working tree. Any changes to tracked files in the working tree since <commit> are discarded.

If you want to actually keep whatever changes you've got locally - do a --soft reset instead. Which will update the commit history for the branch, but not change any files in the working directory (and you can then commit them).

Rebase

You can replay your local commits on top of any other commit/branch using git rebase:

git rebase -i origin/main

This will invoke rebase in interactive mode where you can choose how to apply each individual commit that isn't in the history you are rebasing on top of.

If the commits you removed (with git push -f) have already been pulled into the local history, they will be listed as commits that will be reapplied - they would need to be deleted as part of the rebase or they will simply be re-included into the history for the branch - and reappear in the remote history on the next push.

Use the help git command --help for more details and examples on any of the above (or other) commands.

6
  • 1
    @iblue When your colleague use `git reabse origin/master', and mean while, they already had some commit before, git will write your commit to the behind of their commit.
    – Tim
    Commented Mar 22, 2012 at 6:59
  • 13
    Might be worth mentioning that if this is for a different branch: git reset origin/otherbranch --hard
    – bmaupin
    Commented Feb 1, 2017 at 20:18
  • So, to clarify, this is either: Option 1: reset --hard, or Option 2: reset --soft + rebase, right? Commented Apr 30, 2019 at 16:36
  • 2
    @PlasmaBinturong No. git reset --soft origin/master will change commit history to match the remote and stage differences to the remote which then be committed. There'd be no need to rebase in that scenario (and you'd be prevented from doing so because of the uncommitted changes) because there's no difference in commit history. The two options are reset or rebase - not a combination of both. Please ask a question if your scenario is different than the one I've answered here.
    – AD7six
    Commented May 2, 2019 at 7:19
  • @bmaupin Check out the other branch first! If you want to go with option 1 for a any branch, you have to check it out first. git reset will operate on whatever branch you're currently on.
    – iliis
    Commented Oct 15, 2019 at 10:52
114

Pull with rebase

A regular pull is fetch + merge, but what you want is fetch + rebase. This is an option with the pull command:

git pull --rebase

In your particular case, commits have been removed which you don't want to be reapplied. This has to be done manually. Therefore, the rebase needs to be interactive so these commits can be skipped:

git pull --rebase=interactive

or as of Git 2.26 can be shortened to:

git pull --rebase=i
6
  • 3
    I use this all the time to get the latest code from master into my feature branch without having all those merge commits in the history.
    – herman
    Commented May 28, 2020 at 7:25
  • 6
    This is much simpler than git reset origin/master --hard or git rebase origin/master especially if there are no local commits. Thanks!
    – Dr_Zaszuś
    Commented Nov 4, 2020 at 10:40
  • Thank you! This worked for me when I rebased my feature branch on master, force pushed it and then git pull --rebase d it on another machine.
    – praty
    Commented Nov 10, 2021 at 10:33
  • 3
    I haven't investigated, but I think this will only work correctly if only new commits have been added—rather than old commits being removed or changed. (Say, if one branch was rebased onto another, or the ordering of commits were changed.) Otherwise, you will end up re-adding the removed commits. I think that is the reason for needing git reset.
    – Neil Traft
    Commented Dec 23, 2021 at 23:48
  • 1
    @NeilTraft Indeed that is why the accepted answer includes rebasing in interactive mode. This is also possible in case off pulling with rebase, so I've updated my answer. Reset is not necessary (it is one of the two options in the accepted answer).
    – herman
    Commented Dec 28, 2021 at 10:27
22

This won't fix branches that already have the code you don't want in them (see below for how to do that), but if they had pulled some-branch and now want it to be clean (and not "ahead" of origin/some-branch) then you simply:

git checkout some-branch   # where some-branch can be replaced by any other branch
git branch base-branch -D  # where base-branch is the one with the squashed commits
git checkout -b base-branch origin/base-branch  # recreating branch with correct commits

Note: You can combine these all by putting && between them

Note2: Florian mentioned this in a comment, but who reads comments when looking for answers?

Note3: If you have contaminated branches, you can create new ones based off the new "dumb branch" and just cherry-pick commits over.

Ex:

git checkout feature-old  # some branch with the extra commits
git log                   # gives commits (write down the id of the ones you want)
git checkout base-branch  # after you have already cleaned your local copy of it as above
git checkout -b feature-new # make a new branch for your feature
git cherry-pick asdfasd   # where asdfasd is one of the commit ids you want
# repeat previous step for each commit id
git branch feature-old -D # delete the old branch

Now feature-new is your branch without the extra (possibly bad) commits!

3
  • This is what I really wanted. Someone rebased the master branch (for god knows what reason) but I had no local changes on it that I wanted to commit or anything. So all I had to do was delete my local master branch (which felt really weird) and do a checkout again. Thanks! Commented Jan 2, 2018 at 17:44
  • @peter-mortensen Edits should be substantial according to stackoverflow.com/help/editing
    – Tom Prats
    Commented Jan 16, 2018 at 23:36
  • for the the last step, you can also use git checkout -b base-branch origin/base-branch with git checkout --track origin/base-branch
    – bluesmonk
    Commented Jun 17, 2019 at 19:38
13

To fetch changes from the remote repository into your local repo:

git fetch --all

Rename the tracking branch:

git branch -m <old-branch-name> <new-name>

Create a new local branch tracking the remote branch:

git checkout <old-branch-name>
5
  • I think command 3 should be git checkout <old-branch-name>, right?
    – Daniel
    Commented Nov 9, 2022 at 12:42
  • @Daniel Yeah, it's the third. You just have to execute the commands in the same order as shown in the answer. Commented Nov 10, 2022 at 14:14
  • without -b right
    – Daniel
    Commented Nov 11, 2022 at 10:42
  • @Daniel No -b is required to create a tracking branch (which tracks the changes of the remote branch that has the same name as <old-branch-name>). Commented Nov 12, 2022 at 11:42
  • 1
    @Daniel Sorry you were right. -b flag is not required. Commented Feb 20, 2023 at 6:43
2

TLDR:

git switch master
git pull
git branch -D featureBranch  // careful! FORCE DELETE
git switch featureBranch

So in your case the branches are already tracked locally. Make sure master is checked out. On master git pull is sufficient to update all your branches including your feature branch. You can have a look with git branch -vv to see the branch where a git push --force happened, you will see something like ...ahead 2, behind 7...

Now with git branch -D featureBranch you do a force delete of your local feature branch.

Last step: git switch featureBranch, which checks out the before pulled feature branch locally again and sets it up to track the remote branch.

Not the answer you're looking for? Browse other questions tagged or ask your own question.