git push --force-with-lease
It is usually not recommended to use git --force on a shared branch. Let's see how git push --force-with-lease can help.
Let’s suppose that Alina and Blake are both working on a branch called b1
.
The starting point for Alina is as follows:
Blake makes a change on the main
branch and pushes it.
Her commit graph then looks like this:
Alina is aware of the change and decides to rebase her branch on top of main
.
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), 248 bytes | 124.00 KiB/s, done.
From ssh://remote.mygit.com/gitpowerup/arenas
99d7dab..deebac7 main -> origin/main
alina git:( b1 ) █
This updates her origin/main
branch:
She performs the rebase:
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 ba34ea0... update
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 ba34ea0... update
alina █
Alina resolves the conflicts and completes the rebase.
alina
alina git rebase --continue
Successfully rebased and updated refs/heads/b1.
alina git:( b1 ) █
This gives Alina a new commit graph:
When she tries to git push
, Git indicates that the history has been rewritten and that extra steps are required:
alina
alina git:( b1 ) git push
To ssh://remote.mygit.com/gitpowerup/arenas.git
! [rejected] b1 -> b1 (non-fast-forward)
error: failed to push some refs to 'ssh://remote.mygit.com/gitpowerup/arenas.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
alina git:( b1 ) █
Meanwhile, Blake switches to the old b1
branch.
blake
blake git:( main ) git checkout b1
branch 'b1' set up to track 'origin/b1'.
Switched to a new branch 'b1'
blake git:( b1 ) █
Blake has this commit graph:
She creates a commit and pushes it. Blake’s commit graph is now:
At this stage, the remote server has that commit graph, which is aligned with Blake’s:
Blake has added the line C
to the file file.txt
.
The content of file.txt
for Blake is:
blake
blake git:( b1 ) cat file.txt
A
B
C
blake git:( b1 ) █
Meanwhile, for Alina, the content of file.txt
is:
alina
alina git:( b1 ) cat file.txt
A
A.1
B
alina git:( b1 ) █
Alina decides to git push --force
her rebased b1
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), 270 bytes | 270.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
+ 63d0b3d...37c9f1c b1 -> b1 (forced update)
alina git:( b1 ) █
The remote server now has this commit graph:
Blake does a git fetch --all
to get the latest changes from the remote server:
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), 250 bytes | 125.00 KiB/s, done.
From ssh://remote.mygit.com/gitpowerup/arenas
+ 63d0b3d...37c9f1c b1 -> origin/b1 (forced update)
blake git:( b1 ) █
Her commit tree shows that a divergence occurred:
And a git status
confirms that:
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 ) █
In that case, to resync her b1
branch with the remote, a git reset --hard
is in order:
blake
blake git:( b1 ) git reset --hard origin/b1
HEAD is now at 37c9f1c update
blake git:( b1 ) █
Blake’s commit graph is back in order:
But the content of file.txt
shows that her change is lost (no more C
line):
blake
blake git:( b1 ) cat file.txt
A
A.1
B
blake git:( b1 ) █
Let’s repeat with a safer push
We repeat the process:
- Blake makes a change on the
main
branch and pushes it. - Then she switches back to the
b1
branch. - Alina does a
git fetch --all
and then agit rebase
of herb1
branch.
This is the final step of the rebase:
alina
alina git rebase origin/main--continue
file.txt: needs merge
You must edit all merge conflicts and then
mark them as resolved using git add
alina █
And Alina’s commit graph is:
Alina added the line A.2
to file.txt
.
Alina’s file.txt
content is now:
alina
alina git:( b1 ) cat file.txt
A
A.1
A.2
B
alina git:( b1 ) █
Back on Blake’s machine, she is on the old b1
branch and makes a change that she pushes:
That gives her this commit tree:
And on the remote server, the commit graph is now:
On Blake’s machine, the content of file.txt
is (she added the C
line):
blake
blake git:( b1 ) cat file.txt
A
A.1
B
C
blake git:( b1 ) █
This time, Alina uses git push --force-with-lease
to update the b1
branch on the remote:
alina
alina git:( b1 ) git push --force-with-leasegit push --force-with-lease
To ssh://remote.mygit.com/gitpowerup/arenas.git
! [rejected] b1 -> b1 (stale info)
error: failed to push some refs to 'ssh://remote.mygit.com/gitpowerup/arenas.git'
alina git:( b1 ) █
Git this time indicates that her local state of b1
is not what is currently on the remote and aborts the git push.
To fix that situation, Alina must first retrieve the commits from the remote with a git fetch
:
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), 257 bytes | 128.00 KiB/s, done.
From ssh://remote.mygit.com/gitpowerup/arenas
37c9f1c..106c573 b1 -> origin/b1
alina git:( b1 ) █
That updates Alina’s commit graph:
Alina can rebase her local b1
on top of the remote b1
with git rebase origin/b1
:
alina
alina git:( b1 ) git rebase origin/b1
Rebasing (1/2)
Auto-merging file.txt
CONFLICT (content): Merge conflict in file.txt
error: could not apply 0dba9e1... update
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 0dba9e1... update
alina █
Alina resolves the conflicts:
alina
alina git rebase --continue
Successfully rebased and updated refs/heads/b1.
alina git:( b1 ) █
This leads to the updated commit graph:
And she can push again with git push --force-with-lease
:
alina
alina git:( b1 ) git push --force-with-lease
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
106c573..da4d1e8 b1 -> b1
alina git:( b1 ) █
Alina’s commit graph is now:
And the remote commit graph is now:
On Alina’s machine, the content of file.txt
is:
alina
alina git:( b1 ) cat file.txt
A
A.1
A.2
B
C
alina git:( b1 ) █
On Blake’s machine, the content of that file is:
blake
blake git:( b1 ) cat file.txt
A
A.1
B
C
blake git:( b1 ) █
The line C
is present on both machines, meaning that the changes made by Blake were not lost this time.