How to restore a branch after someone did a git push --force

You are sharing a branch with another developer and this developer did a git push --force and you want to resume work on that branch without losing your local commits.

The initial setup is as follows:

  • A branch b1 has been created out of main.
  • b1 has been pushed to the remote repository.
  • Both Alina and Blake are working on that branch.
  • New commits were pushed on main.

There is only one file being edited: file.txt.

The commit tree is as follows:

arenas.git

417701d91c8e94a9e37e21e99978b1mainHEAD

On main, when the branch b1 was created, the file was at commit a9e37e2.

A

After the branch b1 was created, the file on main was changed to commit 417701d.

A:1

X:24

Alina changes the file on b1 to commit 91c8e94.

A

B

Alina and Blake are both working on that branch from that initial state.

Alina does a git rebase

Alina decides to rebase the branch b1 on main in order to sync with the latest changes on main.

alina

alina git:( b1 ) git rebase origin/main

Rebasing (1/1)

Auto-merging file.txt

CONFLICT (content): Merge conflict in file.txt

error: could not apply 91c8e94... add line B

hint: Resolve all conflicts manually, mark them as resolved with

hint: "git add/rm <conflicted_files>", then run "git rebase --continue".

hint: You can instead skip this commit: run "git rebase --skip".

hint: To abort and get back to the state before "git rebase", run "git rebase --abort".

Could not apply 91c8e94... add line B

alina

Due to the incompatible changes between the two branches, the rebase fails with a conflict and Alina must resolve the conflict manually.

Alina edits the file file.txt and resolves the conflicts by removing the markers while fixing the content:

alina

alina git:( b1 ) cat file.txt

A:1

B

X:24

alina git:( b1 )

Alina then finishes the rebase.

alina

alina git add file.txt

alina git rebase --continue

Successfully rebased and updated refs/heads/b1.

alina git:( b1 )

Because Alina performed a rebase, she must do a push --force to update the remote branch.

alina

alina git:( b1 ) git push --force

Enumerating objects: 5, done.

Counting objects: 100% (5/5), done.

Delta compression using up to 8 threads

Compressing objects: 100% (2/2), done.

Writing objects: 100% (3/3), 279 bytes | 279.00 KiB/s, done.

Total 3 (delta 0), reused 0 (delta 0), pack-reused 0

remote: 

remote: Create a new pull request for 'b1':

remote:   http://localhost:3030/gitpowerup/arenas/compare/main...b1

remote: 

remote: . Processing 1 references

remote: Processed 1 references in total

To ssh://remote.mygit.com/gitpowerup/arenas.git

 + 91c8e94...0f6f7bf b1 -> b1 (forced update)

alina git:( b1 )

And Alina’s commit graph is now:

alina

0f6f7bf417701d91c8e94a9e37e21e99978b1mainorigin/b1origin/mainHEAD

On the server, the commit graph is:

arenas.git

0f6f7bf417701d91c8e94a9e37e21e99978b1mainHEAD

back to Blake

Blake, who is also working on the branch b1, changes the file file.txt to be:

A

B

C

And commits the change.

blake

blake git:( b1 ) git add file.txt

blake git:( b1 ) git commit -m 'add line C'

[b1 9c5b6bd] add line C

 1 file changed, 1 insertion(+)

blake git:( b1 )

But when she tries to push her change, she gets an error.

blake

blake git:( b1 ) git push

To ssh://remote.mygit.com/gitpowerup/arenas.git

 ! [rejected]        b1 -> b1 (fetch first)

error: failed to push some refs to 'ssh://remote.mygit.com/gitpowerup/arenas.git'

hint: Updates were rejected because the remote contains work that you do

hint: not have locally. This is usually caused by another repository pushing

hint: to the same ref. You may want to first integrate the remote changes

hint: (e.g., 'git pull ...') before pushing again.

hint: See the 'Note about fast-forwards' in 'git push --help' for details.

blake git:( b1 )

Blake syncs up her local with a git fetch.

blake

blake git:( b1 ) git fetch --all

remote: Enumerating objects: 5, done.

remote: Counting objects: 100% (5/5), done.

remote: Compressing objects: 100% (2/2), done.

remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0

Unpacking objects: 100% (3/3), 259 bytes | 129.00 KiB/s, done.

From ssh://remote.mygit.com/gitpowerup/arenas

 + 91c8e94...0f6f7bf b1         -> origin/b1  (forced update)

blake git:( b1 )

A git status shows the divergence between her local branch b1 and the remote branch b1:

blake

blake git:( b1 ) git status

On branch b1

Your branch and 'origin/b1' have diverged,

and have 2 and 2 different commits each, respectively.

  (use "git pull" to merge the remote branch into yours)

nothing to commit, working tree clean

blake git:( b1 )

Blake’s commit graph is:

blake

9c5b6bd0f6f7bf417701d91c8e94a9e37e21e99978b1mainorigin/b1origin/mainHEAD

Blake:

  • Has 2 commits that are not on the remote branch b1: 9c5b6bd and 91c8e94.
  • Is missing the 2 commits that are on the remote branch b1: 0f6f7bf and 417701d.

Git suggests performing a git pull:

blake

blake git:( b1 ) git pull

hint: You have divergent branches and need to specify how to reconcile them.

hint: You can do so by running one of the following commands sometime before

hint: your next pull:

hint: 

hint:   git config pull.rebase false  # merge

hint:   git config pull.rebase true   # rebase

hint:   git config pull.ff only       # fast-forward only

hint: 

hint: You can replace "git config" with "git config --global" to set a default

hint: preference for all repositories. You can also pass --rebase, --no-rebase,

hint: or --ff-only on the command line to override the configured default per

hint: invocation.

fatal: Need to specify how to reconcile divergent branches.

blake git:( b1 )

Blake can either merge or rebase her commits onto the remote branch b1. She could also have done a git reset --hard to fully reconcile her local branch b1 with the remote branch b1, but she would have lost her local commits.

Blake performs a rebase.

blake

blake git:( b1 ) git pull --rebase

Rebasing (1/1)

Auto-merging file.txt

CONFLICT (content): Merge conflict in file.txt

error: could not apply 9c5b6bd... add line C

hint: Resolve all conflicts manually, mark them as resolved with

hint: "git add/rm <conflicted_files>", then run "git rebase --continue".

hint: You can instead skip this commit: run "git rebase --skip".

hint: To abort and get back to the state before "git rebase", run "git rebase --abort".

Could not apply 9c5b6bd... add line C

blake

There is also a conflict as git doesn’t know how to add the line C in the file file.txt.

Blake resolves the conflict by editing the file file.txt to be:

alina

alina git:( b1 ) cat file.txt

A:1

B

C

X:24

alina git:( b1 )

Blake finishes the rebase.

blake

blake git add file.txt

blake git rebase --continue

Successfully rebased and updated refs/heads/b1.

blake git:( b1 )

Blake can now push her changes.

blake

blake git:( b1 ) git status

On branch b1

Your branch is ahead of 'origin/b1' by 1 commit.

  (use "git push" to publish your local commits)

nothing to commit, working tree clean

blake git:( b1 ) git push

Enumerating objects: 5, done.

Counting objects: 100% (5/5), done.

Delta compression using up to 8 threads

Compressing objects: 100% (2/2), done.

Writing objects: 100% (3/3), 281 bytes | 281.00 KiB/s, done.

Total 3 (delta 0), reused 0 (delta 0), pack-reused 0

remote: 

remote: Create a new pull request for 'b1':

remote:   http://localhost:3030/gitpowerup/arenas/compare/main...b1

remote: 

remote: . Processing 1 references

remote: Processed 1 references in total

To ssh://remote.mygit.com/gitpowerup/arenas.git

   0f6f7bf..9336d1f  b1 -> b1

blake git:( b1 )

Blake’s commit graph is now:

blake

9336d1f9c5b6bd0f6f7bf417701d91c8e94a9e37e21e99978b1mainorigin/b1origin/mainHEAD

back with Alina

Alina can now retrieve Blake’s change.

alina

alina git:( b1 ) git fetch --all

remote: Enumerating objects: 5, done.

remote: Counting objects: 100% (5/5), done.

remote: Compressing objects: 100% (2/2), done.

remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0

Unpacking objects: 100% (3/3), 261 bytes | 130.00 KiB/s, done.

From ssh://remote.mygit.com/gitpowerup/arenas

   0f6f7bf..9336d1f  b1         -> origin/b1

alina git:( b1 ) git pull

Updating 0f6f7bf..9336d1f

Fast-forward

 file.txt | 1 +

 1 file changed, 1 insertion(+)

alina git:( b1 )

Alina’s commit graph is now in sync with Blake’s.

alina

9336d1f0f6f7bf417701d91c8e94a9e37e21e99978b1mainorigin/b1origin/mainHEAD

And now they have the same file.txt.

alina

alina git:( b1 ) cat file.txt

A:1

B

C

X:24

alina git:( b1 )

Alina goes back to the main branch.

alina

alina git:( b1 ) git switch main

Switched to branch 'main'

Your branch is behind 'origin/main' by 1 commit, and can be fast-forwarded.

  (use "git pull" to update your local branch)

alina git:( main )

Where the file file.txt is still in its original content.

alina

alina git:( main ) cat file.txt

A

alina git:( main )

The git status indicates that the local main branch is behind the remote main branch by 1 commit.

alina

alina git:( main ) git status

On branch main

Your branch is behind 'origin/main' by 1 commit, and can be fast-forwarded.

  (use "git pull" to update your local branch)

nothing to commit, working tree clean

alina git:( main )

Alina syncs up the local branch.

alina

alina git:( main ) git pull

Updating a9e37e2..417701d

Fast-forward

 file.txt | 3 ++-

 1 file changed, 2 insertions(+), 1 deletion(-)

alina git:( main )

The content is now updated.

alina

alina git:( main ) cat file.txt

A:1

X:24

alina git:( main )

As the branch b1 has been rebased on top of main, a fast-forward merge is possible.

alina

alina git:( main ) git merge --ff-only origin/b1

Updating 417701d..9336d1f

Fast-forward

 file.txt | 2 ++

 1 file changed, 2 insertions(+)

alina git:( main )

That command brings the new commit graph on Alina’s machine.

alina

9336d1f0f6f7bf417701d91c8e94a9e37e21e99978b1mainorigin/b1origin/mainHEAD

And now the last content of file.txt is available.

alina

alina git:( main ) cat file.txt

A:1

B

C

X:24

alina git:( main )